Merge "Integrate LogicalDisplayMapper with DisplayFoldController."
diff --git a/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
new file mode 100644
index 0000000..c62269e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 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.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class TextUtilsPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    public static final String TEMPLATE = "Template that combines %s and %d together";
+
+    public String mVar1 = "example";
+    public int mVar2 = 42;
+
+    /**
+     * Measure overhead of formatting a string via {@link String#format}.
+     */
+    @Test
+    public void timeFormatUpstream() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = String.format(TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    /**
+     * Measure overhead of formatting a string via
+     * {@link TextUtils#formatSimple}.
+     */
+    @Test
+    public void timeFormatLocal() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = TextUtils.formatSimple(TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    /**
+     * Measure overhead of formatting a string inline.
+     */
+    @Test
+    public void timeFormatInline() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = "Template that combines " + mVar1 + " and " + mVar2 + " together";
+        }
+    }
+
+    /**
+     * Measure overhead of a passing null-check that uses a lambda to
+     * communicate a custom error message.
+     */
+    @Test
+    public void timeFormat_Skip_Lambda() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            requireNonNull(this, () -> {
+                return String.format(TEMPLATE, mVar1, mVar2);
+            });
+        }
+    }
+
+    /**
+     * Measure overhead of a passing null-check that uses varargs to communicate
+     * a custom error message.
+     */
+    @Test
+    public void timeFormat_Skip_Varargs() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            requireNonNull(this, TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
+        return obj;
+    }
+
+    private static <T> T requireNonNull(T obj, String format, Object... args) {
+        return obj;
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index b11d7464..6f0001d 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -84,6 +84,7 @@
 
     private static class TestWindow extends BaseIWindow {
         final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
+        final InsetsState mRequestedVisibility = new InsetsState();
         final Rect mOutFrame = new Rect();
         final Rect mOutContentInsets = new Rect();
         final Rect mOutStableInsets = new Rect();
@@ -108,7 +109,8 @@
 
                 long startTime = SystemClock.elapsedRealtimeNanos();
                 session.addToDisplay(this, mLayoutParams, View.VISIBLE,
-                        Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
+                        Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame,
+                        mOutContentInsets, mOutStableInsets,
                         mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
                 final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
                 state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/Android.bp b/apex/Android.bp
index 266e672..c5b4901 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -107,7 +107,7 @@
 
     // Hide impl library and stub sources
     impl_library_visibility: [
-        ":__package__",
+        ":__pkg__",
         "//frameworks/base", // For framework-all
     ],
     stubs_source_visibility: ["//visibility:private"],
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index e10fb07..12afde4 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -26,10 +26,8 @@
     srcs: [ ":framework-appsearch-sources" ],
     sdk_version: "core_platform", // TODO(b/146218515) should be module_current
     impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed
-    static_libs: ["icing-java-proto-lite"],
     defaults: ["framework-module-defaults"],
     permitted_packages: ["android.app.appsearch"],
-    jarjar_rules: "jarjar-rules.txt",
     aidl: {
         include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed
     },
diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt
deleted file mode 100644
index acf759a..0000000
--- a/apex/appsearch/framework/jarjar-rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-rule com.google.protobuf.** android.app.appsearch.protobuf.@1
-rule com.google.android.icing.proto.** android.app.appsearch.proto.@1
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index dc75825..98daa66 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -22,6 +22,8 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Collections;
 import java.util.Map;
 
@@ -33,11 +35,11 @@
  * @param <ValueType> The type of result objects associated with the keys.
  * @hide
  */
-public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
+public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
     @NonNull private final Map<KeyType, ValueType> mSuccesses;
     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
 
-    private AppSearchBatchResult(
+    AppSearchBatchResult(
             @NonNull Map<KeyType, ValueType> successes,
             @NonNull Map<KeyType, AppSearchResult<ValueType>> failures) {
         mSuccesses = successes;
@@ -61,8 +63,8 @@
     }
 
     /**
-     * Returns a {@link Map} of all successful keys mapped to the successful {@link ValueType}
-     * values they produced.
+     * Returns a {@link Map} of all successful keys mapped to the successful
+     * {@link AppSearchResult}s they produced.
      *
      * <p>The values of the {@link Map} will not be {@code null}.
      */
@@ -82,6 +84,22 @@
         return mFailures;
     }
 
+    /**
+     * Asserts that this {@link AppSearchBatchResult} has no failures.
+     * @hide
+     */
+    public void checkSuccess() {
+        if (!isSuccess()) {
+            throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
+        }
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "{\n  successes: " + mSuccesses + "\n  failures: " + mFailures + "\n}";
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -112,16 +130,18 @@
     public static final class Builder<KeyType, ValueType> {
         private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
         private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
-
-        /** Creates a new {@link Builder} for this {@link AppSearchBatchResult}. */
-        public Builder() {}
+        private boolean mBuilt = false;
 
         /**
          * Associates the {@code key} with the given successful return value.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
-        public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType result) {
+        @NonNull
+        public Builder<KeyType, ValueType> setSuccess(
+                @NonNull KeyType key, @Nullable ValueType result) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
             return setResult(key, AppSearchResult.newSuccessfulResult(result));
         }
 
@@ -130,10 +150,13 @@
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
-        public Builder setFailure(
+        @NonNull
+        public Builder<KeyType, ValueType> setFailure(
                 @NonNull KeyType key,
                 @AppSearchResult.ResultCode int resultCode,
                 @Nullable String errorMessage) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
             return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
         }
 
@@ -143,7 +166,11 @@
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
         @NonNull
-        public Builder setResult(@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
+        public Builder<KeyType, ValueType> setResult(
+                @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(result);
             if (result.isSuccess()) {
                 mSuccesses.put(key, result.getResultValue());
                 mFailures.remove(key);
@@ -157,6 +184,8 @@
         /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */
         @NonNull
         public AppSearchBatchResult<KeyType, ValueType> build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
             return new AppSearchBatchResult<>(mSuccesses, mFailures);
         }
     }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java
deleted file mode 100644
index 7d2b64e..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (C) 2020 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.app.appsearch;
-
-import android.annotation.CurrentTimeMillisLong;
-import android.annotation.DurationMillisLong;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.icing.proto.DocumentProto;
-import com.google.android.icing.proto.PropertyProto;
-import com.google.protobuf.ByteString;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Represents a document unit.
- *
- * <p>Documents are constructed via {@link AppSearchDocument.Builder}.
- * @hide
- */
-public class AppSearchDocument {
-    private static final String TAG = "AppSearchDocument";
-
-    /** The default empty namespace.*/
-    // TODO(adorokhine): Allow namespace to be specified in the document.
-    public static final String DEFAULT_NAMESPACE = "";
-
-    /**
-     * The maximum number of elements in a repeatable field. Will reject the request if exceed
-     * this limit.
-     */
-    private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
-
-    /**
-     * The maximum {@link String#length} of a {@link String} field. Will reject the request if
-     * {@link String}s longer than this.
-     */
-    private static final int MAX_STRING_LENGTH = 20_000;
-
-    /**
-     * Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and properties
-     * ordered by keys.
-     */
-    @NonNull
-    private final DocumentProto mProto;
-
-    /** Contains all properties in {@link #mProto} to support getting properties via keys. */
-    @NonNull
-    private final Map<String, Object> mProperties;
-
-    /**
-     * Creates a new {@link AppSearchDocument}.
-     * @param proto Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and
-     *               properties ordered by keys.
-     * @param propertiesMap Contains all properties in {@link #mProto} to support get properties
-     *                      via keys.
-     */
-    private AppSearchDocument(@NonNull DocumentProto proto,
-            @NonNull Map<String, Object> propertiesMap) {
-        mProto = proto;
-        mProperties = propertiesMap;
-    }
-
-    /**
-     * Creates a new {@link AppSearchDocument} from an existing instance.
-     *
-     * <p>This method should be only used by constructor of a subclass.
-     */
-    protected AppSearchDocument(@NonNull AppSearchDocument document) {
-        this(document.mProto, document.mProperties);
-    }
-
-    /** @hide */
-    AppSearchDocument(@NonNull DocumentProto documentProto) {
-        this(documentProto, new ArrayMap<>());
-        for (int i = 0; i < documentProto.getPropertiesCount(); i++) {
-            PropertyProto property = documentProto.getProperties(i);
-            String name = property.getName();
-            if (property.getStringValuesCount() > 0) {
-                String[] values = new String[property.getStringValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getStringValues(j);
-                }
-                mProperties.put(name, values);
-            } else if (property.getInt64ValuesCount() > 0) {
-                long[] values = new long[property.getInt64ValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getInt64Values(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getDoubleValuesCount() > 0) {
-                double[] values = new double[property.getDoubleValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getDoubleValues(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getBooleanValuesCount() > 0) {
-                boolean[] values = new boolean[property.getBooleanValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getBooleanValues(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getBytesValuesCount() > 0) {
-                byte[][] values = new byte[property.getBytesValuesCount()][];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getBytesValues(j).toByteArray();
-                }
-                mProperties.put(name, values);
-            } else if (property.getDocumentValuesCount() > 0) {
-                AppSearchDocument[] values =
-                        new AppSearchDocument[property.getDocumentValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = new AppSearchDocument(property.getDocumentValues(j));
-                }
-                mProperties.put(name, values);
-            } else {
-                throw new IllegalStateException("Unknown type of value: " + name);
-            }
-        }
-    }
-
-    /**
-     * Returns the {@link DocumentProto} of the {@link AppSearchDocument}.
-     *
-     * <p>The {@link DocumentProto} contains {@link AppSearchDocument}'s basic information and all
-     *    properties ordered by keys.
-     * @hide
-     */
-    @NonNull
-    @VisibleForTesting
-    public DocumentProto getProto() {
-        return mProto;
-    }
-
-    /** Returns the URI of the {@link AppSearchDocument}. */
-    @NonNull
-    public String getUri() {
-        return mProto.getUri();
-    }
-
-    /** Returns the schema type of the {@link AppSearchDocument}. */
-    @NonNull
-    public String getSchemaType() {
-        return mProto.getSchema();
-    }
-
-    /**
-     * Returns the creation timestamp in milliseconds of the {@link AppSearchDocument}. Value will
-     * be in the {@link System#currentTimeMillis()} time base.
-     */
-    @CurrentTimeMillisLong
-    public long getCreationTimestampMillis() {
-        return mProto.getCreationTimestampMs();
-    }
-
-    /**
-     * Returns the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds.
-     *
-     * <p>The default value is 0, which means the document is permanent and won't be auto-deleted
-     *    until the app is uninstalled.
-     */
-    @DurationMillisLong
-    public long getTtlMillis() {
-        return mProto.getTtlMs();
-    }
-
-    /**
-     * Returns the score of the {@link AppSearchDocument}.
-     *
-     * <p>The score is a query-independent measure of the document's quality, relative to other
-     * {@link AppSearchDocument}s of the same type.
-     *
-     * <p>The default value is 0.
-     */
-    public int getScore() {
-        return mProto.getScore();
-    }
-
-    /**
-     * Retrieve a {@link String} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@link String} associated with the given key or {@code null} if there
-     *         is no such key or the value is of a different type.
-     */
-    @Nullable
-    public String getPropertyString(@NonNull String key) {
-        String[] propertyArray = getPropertyStringArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("String", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code long} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code long} associated with the given key or default value {@code 0} if
-     *         there is no such key or the value is of a different type.
-     */
-    public long getPropertyLong(@NonNull String key) {
-        long[] propertyArray = getPropertyLongArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return 0;
-        }
-        warnIfSinglePropertyTooLong("Long", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code double} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code double} associated with the given key or default value {@code 0.0}
-     *         if there is no such key or the value is of a different type.
-     */
-    public double getPropertyDouble(@NonNull String key) {
-        double[] propertyArray = getPropertyDoubleArray(key);
-        // TODO(tytytyww): Add support double array to ArraysUtils.isEmpty().
-        if (propertyArray == null || propertyArray.length == 0) {
-            return 0.0;
-        }
-        warnIfSinglePropertyTooLong("Double", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code boolean} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code boolean} associated with the given key or default value
-     *         {@code false} if there is no such key or the value is of a different type.
-     */
-    public boolean getPropertyBoolean(@NonNull String key) {
-        boolean[] propertyArray = getPropertyBooleanArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return false;
-        }
-        warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code byte[]} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code byte[]} associated with the given key or {@code null} if there
-     *         is no such key or the value is of a different type.
-     */
-    @Nullable
-    public byte[] getPropertyBytes(@NonNull String key) {
-        byte[][] propertyArray = getPropertyBytesArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@link AppSearchDocument} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@link AppSearchDocument} associated with the given key or {@code null} if
-     *         there is no such key or the value is of a different type.
-     */
-    @Nullable
-    public AppSearchDocument getPropertyDocument(@NonNull String key) {
-        AppSearchDocument[] propertyArray = getPropertyDocumentArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("Document", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /** Prints a warning to logcat if the given propertyLength is greater than 1. */
-    private static void warnIfSinglePropertyTooLong(
-            @NonNull String propertyType, @NonNull String key, int propertyLength) {
-        if (propertyLength > 1) {
-            Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength
-                    + " elements. Only the first one will be returned from "
-                    + "getProperty" + propertyType + "(). Try getProperty" + propertyType
-                    + "Array().");
-        }
-    }
-
-    /**
-     * Retrieve a repeated {@link String} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code String[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public String[] getPropertyStringArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, String[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code long} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code long[]} associated with the given key, or {@code null} if no value is
-     *         set or the value is of a different type.
-     */
-    @Nullable
-    public long[] getPropertyLongArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, long[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code double} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code double[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public double[] getPropertyDoubleArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, double[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code boolean} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code boolean[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public boolean[] getPropertyBooleanArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, boolean[].class);
-    }
-
-    /**
-     * Retrieve a {@code byte[][]} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code byte[][]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public byte[][] getPropertyBytesArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, byte[][].class);
-    }
-
-    /**
-     * Retrieve a repeated {@link AppSearchDocument} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@link AppSearchDocument[]} associated with the given key, or {@code null} if no
-     *         value is set or the value is of a different type.
-     */
-    @Nullable
-    public AppSearchDocument[] getPropertyDocumentArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, AppSearchDocument[].class);
-    }
-
-    /**
-     * Gets a repeated property of the given key, and casts it to the given class type, which
-     * must be an array class type.
-     */
-    @Nullable
-    private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) {
-        Object value = mProperties.get(key);
-        if (value == null) {
-            return null;
-        }
-        try {
-            return tClass.cast(value);
-        } catch (ClassCastException e) {
-            Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e);
-            return null;
-        }
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        // Check only proto's equality is sufficient here since all properties in
-        // mProperties are ordered by keys and stored in proto.
-        if (this == other) {
-            return true;
-        }
-        if (!(other instanceof AppSearchDocument)) {
-            return false;
-        }
-        AppSearchDocument otherDocument = (AppSearchDocument) other;
-        return this.mProto.equals(otherDocument.mProto);
-    }
-
-    @Override
-    public int hashCode() {
-        // Hash only proto is sufficient here since all properties in mProperties are ordered by
-        // keys and stored in proto.
-        return mProto.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return mProto.toString();
-    }
-
-    /**
-     * The builder class for {@link AppSearchDocument}.
-     *
-     * @param <BuilderType> Type of subclass who extend this.
-     */
-    public static class Builder<BuilderType extends Builder> {
-
-        private final Map<String, Object> mProperties = new ArrayMap<>();
-        private final DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
-        private final BuilderType mBuilderTypeInstance;
-
-        /**
-         * Creates a new {@link AppSearchDocument.Builder}.
-         *
-         * <p>The URI is a unique string opaque to AppSearch.
-         *
-         * @param uri The uri of {@link AppSearchDocument}.
-         * @param schemaType The schema type of the {@link AppSearchDocument}. The passed-in
-         *       {@code schemaType} must be defined using {@link AppSearchManager#setSchema} prior
-         *       to inserting a document of this {@code schemaType} into the AppSearch index using
-         *       {@link AppSearchManager#putDocuments(List)}. Otherwise, the document will be
-         *       rejected by {@link AppSearchManager#putDocuments(List)}.
-         */
-        public Builder(@NonNull String uri, @NonNull String schemaType) {
-            mBuilderTypeInstance = (BuilderType) this;
-            mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE);
-            // Set current timestamp for creation timestamp by default.
-            setCreationTimestampMillis(System.currentTimeMillis());
-        }
-
-        /**
-         * Sets the score of the {@link AppSearchDocument}.
-         *
-         * <p>The score is a query-independent measure of the document's quality, relative to
-         * other {@link AppSearchDocument}s of the same type.
-         *
-         * @throws IllegalArgumentException If the provided value is negative.
-         */
-        @NonNull
-        public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
-            if (score < 0) {
-                throw new IllegalArgumentException("Document score cannot be negative.");
-            }
-            mProtoBuilder.setScore(score);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Set the creation timestamp in milliseconds of the {@link AppSearchDocument}. Should be
-         * set using a value obtained from the {@link System#currentTimeMillis()} time base.
-         */
-        @NonNull
-        public BuilderType setCreationTimestampMillis(
-                @CurrentTimeMillisLong long creationTimestampMillis) {
-            mProtoBuilder.setCreationTimestampMs(creationTimestampMillis);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Set the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds.
-         *
-         * <p>After this many milliseconds since the {@link #setCreationTimestampMillis(long)}
-         * creation timestamp}, the document is deleted.
-         *
-         * @param ttlMillis A non-negative duration in milliseconds.
-         * @throws IllegalArgumentException If the provided value is negative.
-         */
-        @NonNull
-        public BuilderType setTtlMillis(@DurationMillisLong long ttlMillis) {
-            Preconditions.checkArgumentNonNegative(
-                    ttlMillis, "Document ttlMillis cannot be negative.");
-            mProtoBuilder.setTtlMs(ttlMillis);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code String} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code String} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull String... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code boolean} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code boolean} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code long} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code long} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull long... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code double} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code double} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull double... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code byte[]} for a property, replacing its previous values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code byte[]} of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@link AppSearchDocument} values for a property, replacing its
-         * previous values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@link AppSearchDocument} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull AppSearchDocument... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull String[] values)
-                throws IllegalArgumentException {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The String at " + i + " is null.");
-                } else if (values[i].length() > MAX_STRING_LENGTH) {
-                    throw new IllegalArgumentException("The String at " + i + " length is: "
-                            + values[i].length()  + ", which exceeds length limit: "
-                            + MAX_STRING_LENGTH + ".");
-                }
-            }
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull boolean[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull double[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull long[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull byte[][] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull AppSearchDocument[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The document at " + i + " is null.");
-                }
-            }
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
-            if (length == 0) {
-                throw new IllegalArgumentException("The input array is empty.");
-            } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
-                throw new IllegalArgumentException(
-                        "Repeated property \"" + key + "\" has length " + length
-                                + ", which exceeds the limit of "
-                                + MAX_REPEATED_PROPERTY_LENGTH);
-            }
-        }
-
-        /** Builds the {@link AppSearchDocument} object. */
-        @NonNull
-        public AppSearchDocument build() {
-            // Build proto by sorting the keys in mProperties to exclude the influence of
-            // order. Therefore documents will generate same proto as long as the contents are
-            // same. Note that the order of repeated fields is still preserved.
-            ArrayList<String> keys = new ArrayList<>(mProperties.keySet());
-            Collections.sort(keys);
-            for (int i = 0; i < keys.size(); i++) {
-                String name = keys.get(i);
-                Object values = mProperties.get(name);
-                PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name);
-                if (values instanceof boolean[]) {
-                    for (boolean value : (boolean[]) values) {
-                        propertyProto.addBooleanValues(value);
-                    }
-                } else if (values instanceof long[]) {
-                    for (long value : (long[]) values) {
-                        propertyProto.addInt64Values(value);
-                    }
-                } else if (values instanceof double[]) {
-                    for (double value : (double[]) values) {
-                        propertyProto.addDoubleValues(value);
-                    }
-                } else if (values instanceof String[]) {
-                    for (String value : (String[]) values) {
-                        propertyProto.addStringValues(value);
-                    }
-                } else if (values instanceof AppSearchDocument[]) {
-                    for (AppSearchDocument value : (AppSearchDocument[]) values) {
-                        propertyProto.addDocumentValues(value.getProto());
-                    }
-                } else if (values instanceof byte[][]) {
-                    for (byte[] value : (byte[][]) values) {
-                        propertyProto.addBytesValues(ByteString.copyFrom(value));
-                    }
-                } else {
-                    throw new IllegalStateException(
-                            "Property \"" + name + "\" has unsupported value type \""
-                                    + values.getClass().getSimpleName() + "\"");
-                }
-                mProtoBuilder.addProperties(propertyProto);
-            }
-            return new AppSearchDocument(mProtoBuilder.build(), mProperties);
-        }
-    }
-}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
index b13dd9f..5f2fabe 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -16,20 +16,26 @@
 
 package android.app.appsearch;
 
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+
 import android.app.appsearch.AppSearchSchema.PropertyConfig;
 
 /**
- * Encapsulates a {@link AppSearchDocument} that represent an email.
+ * Encapsulates a {@link GenericDocument} that represent an email.
  *
- * <p>This class is a higher level implement of {@link AppSearchDocument}.
+ * <p>This class is a higher level implement of {@link GenericDocument}.
  *
  * <p>This class will eventually migrate to Jetpack, where it will become public API.
  *
  * @hide
  */
-public class AppSearchEmail extends AppSearchDocument {
+
+public class AppSearchEmail extends GenericDocument {
+    /** The name of the schema type for {@link AppSearchEmail} documents.*/
+    public static final String SCHEMA_TYPE = "builtin:Email";
+
     private static final String KEY_FROM = "from";
     private static final String KEY_TO = "to";
     private static final String KEY_CC = "cc";
@@ -37,46 +43,43 @@
     private static final String KEY_SUBJECT = "subject";
     private static final String KEY_BODY = "body";
 
-    /** The name of the schema type for {@link AppSearchEmail} documents.*/
-    public static final String SCHEMA_TYPE = "builtin:Email";
-
     public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FROM)
+            .addProperty(new PropertyConfig.Builder(KEY_FROM)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TO)
+            ).addProperty(new PropertyConfig.Builder(KEY_TO)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CC)
+            ).addProperty(new PropertyConfig.Builder(KEY_CC)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BCC)
+            ).addProperty(new PropertyConfig.Builder(KEY_BCC)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_SUBJECT)
+            ).addProperty(new PropertyConfig.Builder(KEY_SUBJECT)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BODY)
+            ).addProperty(new PropertyConfig.Builder(KEY_BODY)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
@@ -87,12 +90,11 @@
 
     /**
      * Creates a new {@link AppSearchEmail} from the contents of an existing
-     * {@link AppSearchDocument}.
+     * {@link GenericDocument}.
      *
-     * @param document The {@link AppSearchDocument} containing the email content.
-     * @hide
+     * @param document The {@link GenericDocument} containing the email content.
      */
-    public AppSearchEmail(@NonNull AppSearchDocument document) {
+    public AppSearchEmail(@NonNull GenericDocument document) {
         super(document);
     }
 
@@ -101,7 +103,6 @@
      *
      * @return Returns the subject of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String getFrom() {
@@ -113,7 +114,6 @@
      *
      * @return Returns the destination addresses of {@link AppSearchEmail} or {@code null} if it's
      *         not been set yet.
-     * @hide
      */
     @Nullable
     public String[] getTo() {
@@ -125,7 +125,6 @@
      *
      * @return Returns the CC list of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String[] getCc() {
@@ -137,7 +136,6 @@
      *
      * @return Returns the BCC list of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String[] getBcc() {
@@ -149,7 +147,6 @@
      *
      * @return Returns the value subject of {@link AppSearchEmail} or {@code null} if it's not been
      *         set yet.
-     * @hide
      */
     @Nullable
     public String getSubject() {
@@ -160,7 +157,6 @@
      * Get the body of {@link AppSearchEmail}.
      *
      * @return Returns the body of {@link AppSearchEmail} or {@code null} if it's not been set yet.
-     * @hide
      */
     @Nullable
     public String getBody() {
@@ -169,14 +165,12 @@
 
     /**
      * The builder class for {@link AppSearchEmail}.
-     * @hide
      */
-    public static class Builder extends AppSearchDocument.Builder<AppSearchEmail.Builder> {
+    public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
 
         /**
          * Create a new {@link AppSearchEmail.Builder}
          * @param uri The Uri of the Email.
-         * @hide
          */
         public Builder(@NonNull String uri) {
             super(uri, SCHEMA_TYPE);
@@ -184,7 +178,6 @@
 
         /**
          * Set the from address of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setFrom(@NonNull String from) {
@@ -194,7 +187,6 @@
 
         /**
          * Set the destination address of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setTo(@NonNull String... to) {
@@ -204,7 +196,6 @@
 
         /**
          * Set the CC list of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setCc(@NonNull String... cc) {
@@ -214,7 +205,6 @@
 
         /**
          * Set the BCC list of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setBcc(@NonNull String... bcc) {
@@ -224,7 +214,6 @@
 
         /**
          * Set the subject of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setSubject(@NonNull String subject) {
@@ -234,7 +223,6 @@
 
         /**
          * Set the body of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setBody(@NonNull String body) {
@@ -242,11 +230,7 @@
             return this;
         }
 
-        /**
-         * Builds the {@link AppSearchEmail} object.
-         *
-         * @hide
-         */
+        /** Builds the {@link AppSearchEmail} object. */
         @NonNull
         @Override
         public AppSearchEmail build() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index ad51a5c..18bc59b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -22,14 +22,9 @@
 import android.os.RemoteException;
 
 import com.android.internal.infra.AndroidFuture;
-
-import com.google.android.icing.proto.DocumentProto;
-import com.google.android.icing.proto.SearchResultProto;
-import com.google.android.icing.proto.StatusProto;
-import com.google.protobuf.InvalidProtocolBufferException;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -45,6 +40,7 @@
 // TODO(b/148046169): This class header needs a detailed example/tutorial.
 @SystemService(Context.APP_SEARCH_SERVICE)
 public class AppSearchManager {
+    private static final String DEFAULT_DATABASE = "";
     private final IAppSearchManager mService;
 
     /** @hide */
@@ -78,8 +74,8 @@
      *     <li>Removal of an existing type
      *     <li>Removal of a property from a type
      *     <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
-     *     <li>For properties of {@code AppSearchDocument} type, changing the schema type of
-     *         {@code AppSearchDocument}s of that property
+     *     <li>For properties of {@code GenericDocument} type, changing the schema type of
+     *         {@code GenericDocument}s of that property
      *     <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an
      *         {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL
      *             OPTIONAL} property into a
@@ -101,46 +97,23 @@
      * <p>It is a no-op to set the same schema as has been previously set; this is handled
      * efficiently.
      *
-     * @param schemas The schema configs for the types used by the calling app.
+     * @param request The schema update request.
      * @return the result of performing this operation.
      *
      * @hide
      */
     @NonNull
-    public AppSearchResult<Void> setSchema(@NonNull AppSearchSchema... schemas) {
-        return setSchema(Arrays.asList(schemas), /*forceOverride=*/false);
-    }
-
-    /**
-     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
-     *
-     * <p>This method is similar to {@link #setSchema(AppSearchSchema...)}, except for the
-     * {@code forceOverride} parameter. If a backwards-incompatible schema is specified but the
-     * {@code forceOverride} parameter is set to {@code true}, instead of returning an
-     * {@link AppSearchResult} with the {@link AppSearchResult#RESULT_INVALID_SCHEMA} code, all
-     * documents which are not compatible with the new schema will be deleted and the incompatible
-     * schema will be applied.
-     *
-     * @param schemas The schema configs for the types used by the calling app.
-     * @param forceOverride Whether to force the new schema to be applied even if there are
-     *     incompatible changes versus the previously set schema. Documents which are incompatible
-     *     with the new schema will be deleted.
-     * @return the result of performing this operation.
-     *
-     * @hide
-     */
-    @NonNull
-    public AppSearchResult<Void> setSchema(
-            @NonNull List<AppSearchSchema> schemas, boolean forceOverride) {
+    public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) {
+        Preconditions.checkNotNull(request);
         // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
         //  avoid binder limits.
-        List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
-        for (AppSearchSchema schema : schemas) {
+        List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
+        for (AppSearchSchema schema : request.getSchemas()) {
             schemaBundles.add(schema.getBundle());
         }
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.setSchema(schemaBundles, forceOverride, future);
+            mService.setSchema(DEFAULT_DATABASE, schemaBundles, request.isForceOverride(), future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -148,31 +121,32 @@
     }
 
     /**
-     * Index {@link AppSearchDocument}s into AppSearch.
+     * Index {@link GenericDocument}s into AppSearch.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#putDocuments()} API provided by JetPack.
      *
-     * <p>Each {@link AppSearchDocument}'s {@code schemaType} field must be set to the name of a
+     * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
      * schema type previously registered via the {@link #setSchema} method.
      *
-     * @param documents {@link AppSearchDocument}s that need to be indexed.
-     * @return An {@link AppSearchBatchResult} mapping the document URIs to {@link Void} if they
-     *     were successfully indexed, or a {@link Throwable} describing the failure if they could
-     *     not be indexed.
+     * @param request {@link PutDocumentsRequest} containing documents to be indexed
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the URIs of the input documents. The values are
+     * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult}
+     * otherwise.
      * @hide
      */
-    public AppSearchBatchResult<String, Void> putDocuments(
-            @NonNull List<AppSearchDocument> documents) {
+    public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
         // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
         // one big list.
-        List<byte[]> documentsBytes = new ArrayList<>(documents.size());
-        for (AppSearchDocument document : documents) {
-            documentsBytes.add(document.getProto().toByteArray());
+        List<GenericDocument> documents = request.getDocuments();
+        List<Bundle> documentBundles = new ArrayList<>(documents.size());
+        for (int i = 0; i < documents.size(); i++) {
+            documentBundles.add(documents.get(i).getBundle());
         }
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.putDocuments(documentsBytes, future);
+            mService.putDocuments(DEFAULT_DATABASE, documentBundles, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -180,65 +154,59 @@
     }
 
     /**
-     * Retrieves {@link AppSearchDocument}s by URI.
+     * Retrieves {@link GenericDocument}s by URI.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#getDocuments()} API provided by JetPack.
      *
-     * @param uris URIs of the documents to look up.
-     * @return An {@link AppSearchBatchResult} mapping the document URIs to
-     *     {@link AppSearchDocument} values if they were successfully retrieved, a {@code null}
-     *     failure if they were not found, or a {@link Throwable} failure describing the problem if
-     *     an error occurred.
+     * @param request {@link GetByUriRequest} containing URIs to be retrieved.
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the input URIs. The values are the returned
+     * {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise.
+     * URIs that are not found will return a failed {@link AppSearchResult} with a result code
+     * of {@link AppSearchResult#RESULT_NOT_FOUND}.
      */
-    public AppSearchBatchResult<String, AppSearchDocument> getDocuments(
-            @NonNull List<String> uris) {
+    public AppSearchBatchResult<String, GenericDocument> getByUri(
+            @NonNull GetByUriRequest request) {
         // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
         //     them in one big list.
+        List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.getDocuments(uris, future);
+            mService.getDocuments(DEFAULT_DATABASE, request.getNamespace(), uris, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
 
-        // Deserialize the protos into Document objects
-        AppSearchBatchResult<String, byte[]> protoResults = getFutureOrThrow(future);
-        AppSearchBatchResult.Builder<String, AppSearchDocument> documentResultBuilder =
+        // Translate from document bundles to GenericDocument instances
+        AppSearchBatchResult<String, Bundle> bundleResult = getFutureOrThrow(future);
+        AppSearchBatchResult.Builder<String, GenericDocument> documentResultBuilder =
                 new AppSearchBatchResult.Builder<>();
 
         // Translate successful results
-        for (Map.Entry<String, byte[]> protoResult : protoResults.getSuccesses().entrySet()) {
-            DocumentProto documentProto;
+        for (Map.Entry<String, Bundle> bundleEntry : bundleResult.getSuccesses().entrySet()) {
+            GenericDocument document;
             try {
-                documentProto = DocumentProto.parseFrom(protoResult.getValue());
-            } catch (InvalidProtocolBufferException e) {
-                documentResultBuilder.setFailure(
-                        protoResult.getKey(), AppSearchResult.RESULT_IO_ERROR, e.getMessage());
-                continue;
-            }
-            AppSearchDocument document;
-            try {
-                document = new AppSearchDocument(documentProto);
+                document = new GenericDocument(bundleEntry.getValue());
             } catch (Throwable t) {
                 // These documents went through validation, so how could this fail? We must have
                 // done something wrong.
                 documentResultBuilder.setFailure(
-                        protoResult.getKey(),
+                        bundleEntry.getKey(),
                         AppSearchResult.RESULT_INTERNAL_ERROR,
                         t.getMessage());
                 continue;
             }
-            documentResultBuilder.setSuccess(protoResult.getKey(), document);
+            documentResultBuilder.setSuccess(bundleEntry.getKey(), document);
         }
 
         // Translate failed results
-        for (Map.Entry<String, AppSearchResult<byte[]>> protoResult :
-                protoResults.getFailures().entrySet()) {
+        for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry :
+                bundleResult.getFailures().entrySet()) {
             documentResultBuilder.setFailure(
-                    protoResult.getKey(),
-                    protoResult.getValue().getResultCode(),
-                    protoResult.getValue().getErrorMessage());
+                    bundleEntry.getKey(),
+                    bundleEntry.getValue().getResultCode(),
+                    bundleEntry.getValue().getErrorMessage());
         }
 
         return documentResultBuilder.build();
@@ -287,56 +255,46 @@
      * @hide
      */
     @NonNull
-    public AppSearchResult<SearchResults> query(
+    public AppSearchResult<List<SearchResult>> query(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
         //     them in one big list.
-        AndroidFuture<AppSearchResult> searchResultFuture = new AndroidFuture<>();
+        AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>();
         try {
-            mService.query(queryExpression, searchSpec.getBundle(), searchResultFuture);
+            mService.query(DEFAULT_DATABASE, queryExpression,
+                    searchSpec.getBundle(), searchResultsFuture);
         } catch (RemoteException e) {
-            searchResultFuture.completeExceptionally(e);
+            searchResultsFuture.completeExceptionally(e);
         }
 
-        // Deserialize the protos into Document objects
-        AppSearchResult<byte[]> searchResultBytes = getFutureOrThrow(searchResultFuture);
-        if (!searchResultBytes.isSuccess()) {
+        // Translate the list of Bundle into a list of SearchResult
+        AppSearchResult<SearchResults> searchResultsResult = getFutureOrThrow(searchResultsFuture);
+        if (!searchResultsResult.isSuccess()) {
             return AppSearchResult.newFailedResult(
-                    searchResultBytes.getResultCode(), searchResultBytes.getErrorMessage());
+                    searchResultsResult.getResultCode(), searchResultsResult.getErrorMessage());
         }
-        SearchResultProto searchResultProto;
-        try {
-            searchResultProto = SearchResultProto.parseFrom(searchResultBytes.getResultValue());
-        } catch (InvalidProtocolBufferException e) {
-            return AppSearchResult.newFailedResult(
-                    AppSearchResult.RESULT_INTERNAL_ERROR, e.getMessage());
-        }
-        if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) {
-            // This should never happen; AppSearchManagerService should catch failed searchResults
-            // entries and transmit them as a failed AppSearchResult.
-            return AppSearchResult.newFailedResult(
-                    AppSearchResult.RESULT_INTERNAL_ERROR,
-                    searchResultProto.getStatus().getMessage());
-        }
-
-        return AppSearchResult.newSuccessfulResult(new SearchResults(searchResultProto));
+        SearchResults searchResults = searchResultsResult.getResultValue();
+        return AppSearchResult.newSuccessfulResult(searchResults.mResults);
     }
 
     /**
-     * Deletes {@link AppSearchDocument}s by URI.
+     * Deletes {@link GenericDocument}s by URI.
      *
      * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API
      * provided by JetPack.
      *
-     * @param uris URIs of the documents to delete
-     * @return An {@link AppSearchBatchResult} mapping each URI to a {@code null} success if
-     *     deletion was successful, to a {@code null} failure if the document did not exist, or to a
-     *     {@code throwable} failure if deletion failed for another reason.
+     * @param request Request containing URIs to be removed.
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the input URIs. The values are {@code null} on success,
+     * or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a
+     * failed {@link AppSearchResult} with a result code of
+     * {@link AppSearchResult#RESULT_NOT_FOUND}.
      */
-    public AppSearchBatchResult<String, Void> delete(@NonNull List<String> uris) {
+    public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
+        List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.delete(uris, future);
+            mService.delete(DEFAULT_DATABASE, request.getNamespace(), uris, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -344,7 +302,7 @@
     }
 
     /**
-     * Deletes {@link android.app.appsearch.AppSearch.Document}s by schema type.
+     * Deletes {@link android.app.appsearch.GenericDocument}s by schema type.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#deleteByType()} API provided by JetPack.
@@ -357,7 +315,7 @@
     public AppSearchBatchResult<String, Void> deleteByTypes(@NonNull List<String> schemaTypes) {
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.deleteByTypes(schemaTypes, future);
+            mService.deleteByTypes(DEFAULT_DATABASE, schemaTypes, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -368,7 +326,7 @@
     public AppSearchResult<Void> deleteAll() {
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.deleteAll(future);
+            mService.deleteAll(DEFAULT_DATABASE, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index 7f38348..979eab9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -32,9 +32,12 @@
  * @param <ValueType> The type of result object for successful calls.
  * @hide
  */
-public class AppSearchResult<ValueType> implements Parcelable {
-    /** Result codes from {@link AppSearchManager} methods. */
-    @IntDef(prefix = {"RESULT_"}, value = {
+public final class AppSearchResult<ValueType> implements Parcelable {
+    /**
+     * Result codes from {@link AppSearchManager} methods.
+     * @hide
+     */
+    @IntDef(value = {
             RESULT_OK,
             RESULT_UNKNOWN_ERROR,
             RESULT_INTERNAL_ERROR,
@@ -120,15 +123,18 @@
     }
 
     /**
-     * Returns the returned value associated with this result.
+     * Returns the result value associated with this result, if it was successful.
      *
-     * <p>If {@link #isSuccess} is {@code false}, the result value is always {@code null}. The value
-     * may be {@code null} even if {@link #isSuccess} is {@code true}. See the documentation of the
-     * particular {@link AppSearchManager} call producing this {@link AppSearchResult} for what is
-     * returned by {@link #getResultValue}.
+     * <p>See the documentation of the particular {@link AppSearchManager} call producing this
+     * {@link AppSearchResult} for what is placed in the result value by that call.
+     *
+     * @throws IllegalStateException if this {@link AppSearchResult} is not successful.
      */
     @Nullable
     public ValueType getResultValue() {
+        if (!isSuccess()) {
+            throw new IllegalStateException("AppSearchResult is a failure: " + this);
+        }
         return mResultValue;
     }
 
@@ -146,14 +152,14 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
         if (!(other instanceof AppSearchResult)) {
             return false;
         }
-        AppSearchResult<?> otherResult = (AppSearchResult) other;
+        AppSearchResult<?> otherResult = (AppSearchResult<?>) other;
         return mResultCode == otherResult.mResultCode
                 && Objects.equals(mResultValue, otherResult.mResultValue)
                 && Objects.equals(mErrorMessage, otherResult.mErrorMessage);
@@ -168,9 +174,9 @@
     @NonNull
     public String toString() {
         if (isSuccess()) {
-            return "AppSearchResult [SUCCESS]: " + mResultValue;
+            return "[SUCCESS]: " + mResultValue;
         }
-        return "AppSearchResult [FAILURE(" + mResultCode + ")]: " + mErrorMessage;
+        return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
     }
 
     @Override
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
new file mode 100644
index 0000000..9fe2c67
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.util.Log;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.app.appsearch.exceptions.AppSearchException;
+import com.android.internal.util.Preconditions;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Represents a document unit.
+ *
+ * <p>Documents are constructed via {@link GenericDocument.Builder}.
+ * @hide
+ */
+public class GenericDocument {
+    private static final String TAG = "GenericDocument";
+
+    /** The default empty namespace.*/
+    public static final String DEFAULT_NAMESPACE = "";
+
+    /**
+     * The maximum number of elements in a repeatable field. Will reject the request if exceed
+     * this limit.
+     */
+    private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
+
+    /**
+     * The maximum {@link String#length} of a {@link String} field. Will reject the request if
+     * {@link String}s longer than this.
+     */
+    private static final int MAX_STRING_LENGTH = 20_000;
+
+    /** The maximum number of indexed properties a document can have. */
+    private static final int MAX_INDEXED_PROPERTIES = 16;
+
+    /** The default score of document. */
+    private static final int DEFAULT_SCORE = 0;
+
+    /** The default time-to-live in millisecond of a document, which is infinity. */
+    private static final long DEFAULT_TTL_MILLIS = 0L;
+
+    /** @hide */
+    
+    public static final String PROPERTIES_FIELD = "properties";
+
+    /** @hide */
+    
+    public static final String BYTE_ARRAY_FIELD = "byteArray";
+
+    static final String SCHEMA_TYPE_FIELD = "schemaType";
+    static final String URI_FIELD = "uri";
+    static final String SCORE_FIELD = "score";
+    static final String TTL_MILLIS_FIELD = "ttlMillis";
+    static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis";
+    static final String NAMESPACE_FIELD = "namespace";
+
+    /**
+     * The maximum number of indexed properties a document can have.
+     *
+     * <p>Indexed properties are properties where the
+     * {@link android.app.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is
+     * anything other than {@link
+     * android.app.appsearch.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
+     */
+    public static int getMaxIndexedProperties() {
+        return MAX_INDEXED_PROPERTIES;
+    }
+
+    /** Contains {@link GenericDocument} basic information (uri, schemaType etc).*/
+    @NonNull
+    final Bundle mBundle;
+
+    /** Contains all properties in {@link GenericDocument} to support getting properties via keys.*/
+    @NonNull
+    private final Bundle mProperties;
+
+    @NonNull
+    private final String mUri;
+    @NonNull
+    private final String mSchemaType;
+    private final long mCreationTimestampMillis;
+    @Nullable
+    private Integer mHashCode;
+
+    /**
+     * Rebuilds a {@link GenericDocument} by the a bundle.
+     * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and
+     *               a properties bundle contains all properties in {@link GenericDocument} to
+     *               support getting properties via keys.
+     * @hide
+     */
+    
+    public GenericDocument(@NonNull Bundle bundle) {
+        Preconditions.checkNotNull(bundle);
+        mBundle = bundle;
+        mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD));
+        mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD));
+        mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+        mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD,
+                System.currentTimeMillis());
+    }
+
+    /**
+     * Creates a new {@link GenericDocument} from an existing instance.
+     *
+     * <p>This method should be only used by constructor of a subclass.
+     */
+    protected GenericDocument(@NonNull GenericDocument document) {
+        this(document.mBundle);
+    }
+
+    /**
+     * Returns the {@link Bundle} populated by this builder.
+     * @hide
+     */
+    
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /** Returns the URI of the {@link GenericDocument}. */
+    @NonNull
+    public String getUri() {
+        return mUri;
+    }
+
+    /** Returns the namespace of the {@link GenericDocument}. */
+    @NonNull
+    public String getNamespace() {
+        return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+    }
+
+    /** Returns the schema type of the {@link GenericDocument}. */
+    @NonNull
+    public String getSchemaType() {
+        return mSchemaType;
+    }
+
+    /** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */
+    public long getCreationTimestampMillis() {
+        return mCreationTimestampMillis;
+    }
+
+    /**
+     * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+     *
+     * <p>The default value is 0, which means the document is permanent and won't be auto-deleted
+     *    until the app is uninstalled.
+     */
+    public long getTtlMillis() {
+        return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+    }
+
+    /**
+     * Returns the score of the {@link GenericDocument}.
+     *
+     * <p>The score is a query-independent measure of the document's quality, relative to other
+     * {@link GenericDocument}s of the same type.
+     *
+     * <p>The default value is 0.
+     */
+    public int getScore() {
+        return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE);
+    }
+
+    /**
+     * Retrieves a {@link String} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@link String} associated with the given key or {@code null} if there
+     *         is no such key or the value is of a different type.
+     */
+    @Nullable
+    public String getPropertyString(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        String[] propertyArray = getPropertyStringArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("String", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code long} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code long} associated with the given key or default value {@code 0} if
+     *         there is no such key or the value is of a different type.
+     */
+    public long getPropertyLong(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        long[] propertyArray = getPropertyLongArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return 0;
+        }
+        warnIfSinglePropertyTooLong("Long", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code double} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code double} associated with the given key or default value {@code 0.0}
+     *         if there is no such key or the value is of a different type.
+     */
+    public double getPropertyDouble(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        double[] propertyArray = getPropertyDoubleArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return 0.0;
+        }
+        warnIfSinglePropertyTooLong("Double", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code boolean} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code boolean} associated with the given key or default value
+     *         {@code false} if there is no such key or the value is of a different type.
+     */
+    public boolean getPropertyBoolean(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        boolean[] propertyArray = getPropertyBooleanArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return false;
+        }
+        warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code byte[]} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code byte[]} associated with the given key or {@code null} if there
+     *         is no such key or the value is of a different type.
+     */
+    @Nullable
+    public byte[] getPropertyBytes(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        byte[][] propertyArray = getPropertyBytesArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@link GenericDocument} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@link GenericDocument} associated with the given key or {@code null} if
+     *         there is no such key or the value is of a different type.
+     */
+    @Nullable
+    public GenericDocument getPropertyDocument(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        GenericDocument[] propertyArray = getPropertyDocumentArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("Document", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /** Prints a warning to logcat if the given propertyLength is greater than 1. */
+    private static void warnIfSinglePropertyTooLong(
+            @NonNull String propertyType, @NonNull String key, int propertyLength) {
+        if (propertyLength > 1) {
+            Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength
+                    + " elements. Only the first one will be returned from "
+                    + "getProperty" + propertyType + "(). Try getProperty" + propertyType
+                    + "Array().");
+        }
+    }
+
+    /**
+     * Retrieves a repeated {@code String} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code String[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public String[] getPropertyStringArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, String[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@link String} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code long[]} associated with the given key, or {@code null} if no value is
+     *         set or the value is of a different type.
+     */
+    @Nullable
+    public long[] getPropertyLongArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, long[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@code double} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code double[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public double[] getPropertyDoubleArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, double[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@code boolean} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code boolean[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public boolean[] getPropertyBooleanArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, boolean[].class);
+    }
+
+    /**
+     * Retrieves a {@code byte[][]} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code byte[][]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @SuppressLint("ArrayReturn")
+    @Nullable
+    @SuppressWarnings("unchecked")
+    public byte[][] getPropertyBytesArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class);
+        if (bundles == null || bundles.size() == 0) {
+            return null;
+        }
+        byte[][] bytes = new byte[bundles.size()][];
+        for (int i = 0; i < bundles.size(); i++) {
+            Bundle bundle = bundles.get(i);
+            if (bundle == null) {
+                Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
+                continue;
+            }
+            byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD);
+            if (innerBytes == null) {
+                Log.e(TAG, "The bundle at " + i + " contains a null byte[].");
+                continue;
+            }
+            bytes[i] = innerBytes;
+        }
+        return bytes;
+    }
+
+    /**
+     * Retrieves a repeated {@link GenericDocument} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no
+     *         value is set or the value is of a different type.
+     */
+    @SuppressLint("ArrayReturn")
+    @Nullable
+    public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        Bundle[] bundles = getAndCastPropertyArray(key, Bundle[].class);
+        if (bundles == null || bundles.length == 0) {
+            return null;
+        }
+        GenericDocument[] documents = new GenericDocument[bundles.length];
+        for (int i = 0; i < bundles.length; i++) {
+            if (bundles[i] == null) {
+                Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
+                continue;
+            }
+            documents[i] = new GenericDocument(bundles[i]);
+        }
+        return documents;
+    }
+
+    /**
+     * Gets a repeated property of the given key, and casts it to the given class type, which
+     * must be an array class type.
+     */
+    @Nullable
+    private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) {
+        Object value = mProperties.get(key);
+        if (value == null) {
+            return null;
+        }
+        try {
+            return tClass.cast(value);
+        } catch (ClassCastException e) {
+            Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        // Check only proto's equality is sufficient here since all properties in
+        // mProperties are ordered by keys and stored in proto.
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof GenericDocument)) {
+            return false;
+        }
+        GenericDocument otherDocument = (GenericDocument) other;
+        return bundleEquals(this.mBundle, otherDocument.mBundle);
+    }
+
+    /**
+     * Deeply checks two bundle is equally or not.
+     * <p> Two bundle will be considered equally if they contains same content.
+     */
+    @SuppressWarnings("unchecked")
+    private static boolean bundleEquals(Bundle one, Bundle two) {
+        if (one.size() != two.size()) {
+            return false;
+        }
+        Set<String> keySetOne = one.keySet();
+        Object valueOne;
+        Object valueTwo;
+        // Bundle inherit its equals() from Object.java, which only compare their memory address.
+        // We should iterate all keys and check their presents and values in both bundle.
+        for (String key : keySetOne) {
+            valueOne = one.get(key);
+            valueTwo = two.get(key);
+            if (valueOne instanceof Bundle
+                    && valueTwo instanceof Bundle
+                    && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) {
+                return false;
+            } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) {
+                // If we call bundle.get(key) when the 'key' doesn't actually exist in the
+                // bundle, we'll get  back a null. So make sure that both values are null and
+                // both keys exist in the bundle.
+                return false;
+            } else if (valueOne instanceof boolean[]) {
+                if (!(valueTwo instanceof boolean[])
+                        || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof long[]) {
+                if (!(valueTwo instanceof long[])
+                        || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof double[]) {
+                if (!(valueTwo instanceof double[])
+                        || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof Bundle[]) {
+                if (!(valueTwo instanceof Bundle[])) {
+                    return false;
+                }
+                Bundle[] bundlesOne = (Bundle[]) valueOne;
+                Bundle[] bundlesTwo = (Bundle[]) valueTwo;
+                if (bundlesOne.length != bundlesTwo.length) {
+                    return false;
+                }
+                for (int i = 0; i < bundlesOne.length; i++) {
+                    if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) {
+                        return false;
+                    }
+                }
+            } else if (valueOne instanceof ArrayList) {
+                if (!(valueTwo instanceof ArrayList)) {
+                    return false;
+                }
+                ArrayList<Bundle> bundlesOne = (ArrayList<Bundle>) valueOne;
+                ArrayList<Bundle> bundlesTwo = (ArrayList<Bundle>) valueTwo;
+                if (bundlesOne.size() != bundlesTwo.size()) {
+                    return false;
+                }
+                for (int i = 0; i < bundlesOne.size(); i++) {
+                    if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) {
+                        return false;
+                    }
+                }
+            } else if (valueOne instanceof Object[]) {
+                if (!(valueTwo instanceof Object[])
+                        || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = bundleHashCode(mBundle);
+        }
+        return mHashCode;
+    }
+
+    /**
+     * Calculates the hash code for a bundle.
+     * <p> The hash code is only effected by the content in the bundle. Bundles will get
+     * consistent hash code if they have same content.
+     */
+    @SuppressWarnings("unchecked")
+    private static int bundleHashCode(Bundle bundle) {
+        int[] hashCodes = new int[bundle.size()];
+        int i = 0;
+        // Bundle inherit its hashCode() from Object.java, which only relative to their memory
+        // address. Bundle doesn't have an order, so we should iterate all keys and combine
+        // their value's hashcode into an array. And use the hashcode of the array to be
+        // the hashcode of the bundle.
+        for (String key : bundle.keySet()) {
+            Object value = bundle.get(key);
+            if (value instanceof boolean[]) {
+                hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+            } else if (value instanceof long[]) {
+                hashCodes[i++] = Arrays.hashCode((long[]) value);
+            } else if (value instanceof double[]) {
+                hashCodes[i++] = Arrays.hashCode((double[]) value);
+            } else if (value instanceof String[]) {
+                hashCodes[i++] = Arrays.hashCode((Object[]) value);
+            } else if (value instanceof Bundle) {
+                hashCodes[i++] = bundleHashCode((Bundle) value);
+            } else if (value instanceof Bundle[]) {
+                Bundle[] bundles = (Bundle[]) value;
+                int[] innerHashCodes = new int[bundles.length];
+                for (int j = 0; j < innerHashCodes.length; j++) {
+                    innerHashCodes[j] = bundleHashCode(bundles[j]);
+                }
+                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+            } else if (value instanceof ArrayList) {
+                ArrayList<Bundle> bundles = (ArrayList<Bundle>) value;
+                int[] innerHashCodes = new int[bundles.size()];
+                for (int j = 0; j < innerHashCodes.length; j++) {
+                    innerHashCodes[j] = bundleHashCode(bundles.get(j));
+                }
+                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+            } else {
+                hashCodes[i++] = value.hashCode();
+            }
+        }
+        return Arrays.hashCode(hashCodes);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return bundleToString(mBundle).toString();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static StringBuilder bundleToString(Bundle bundle) {
+        StringBuilder stringBuilder = new StringBuilder();
+        try {
+            final Set<String> keySet = bundle.keySet();
+            String[] keys = keySet.toArray(new String[0]);
+            // Sort keys to make output deterministic. We need a custom comparator to handle
+            // nulls (arbitrarily putting them first, similar to Comparator.nullsFirst, which is
+            // only available since N).
+            Arrays.sort(
+                    keys,
+                    (@Nullable String s1, @Nullable String s2) -> {
+                        if (s1 == null) {
+                            return s2 == null ? 0 : -1;
+                        } else if (s2 == null) {
+                            return 1;
+                        } else {
+                            return s1.compareTo(s2);
+                        }
+                    });
+            for (String key : keys) {
+                stringBuilder.append("{ key: '").append(key).append("' value: ");
+                Object valueObject = bundle.get(key);
+                if (valueObject == null) {
+                    stringBuilder.append("<null>");
+                } else if (valueObject instanceof Bundle) {
+                    stringBuilder.append(bundleToString((Bundle) valueObject));
+                } else if (valueObject.getClass().isArray()) {
+                    stringBuilder.append("[ ");
+                    for (int i = 0; i < Array.getLength(valueObject); i++) {
+                        Object element = Array.get(valueObject, i);
+                        stringBuilder.append("'");
+                        if (element instanceof Bundle) {
+                            stringBuilder.append(bundleToString((Bundle) element));
+                        } else {
+                            stringBuilder.append(Array.get(valueObject, i));
+                        }
+                        stringBuilder.append("' ");
+                    }
+                    stringBuilder.append("]");
+                } else if (valueObject instanceof ArrayList) {
+                    for (Bundle innerBundle : (ArrayList<Bundle>) valueObject) {
+                        stringBuilder.append(bundleToString(innerBundle));
+                    }
+                } else {
+                    stringBuilder.append(valueObject.toString());
+                }
+                stringBuilder.append(" } ");
+            }
+        } catch (RuntimeException e) {
+            // Catch any exceptions here since corrupt Bundles can throw different types of
+            // exceptions (e.g. b/38445840 & b/68937025).
+            stringBuilder.append("<error>");
+        }
+        return stringBuilder;
+    }
+
+    /**
+     * The builder class for {@link GenericDocument}.
+     *
+     * @param <BuilderType> Type of subclass who extend this.
+     */
+    public static class Builder<BuilderType extends Builder> {
+
+        private final Bundle mProperties = new Bundle();
+        private final Bundle mBundle = new Bundle();
+        private final BuilderType mBuilderTypeInstance;
+        private boolean mBuilt = false;
+
+        /**
+         * Create a new {@link GenericDocument.Builder}.
+         *
+         * @param uri The uri of {@link GenericDocument}.
+         * @param schemaType The schema type of the {@link GenericDocument}. The passed-in
+         *        {@code schemaType} must be defined using {@code AppSearchManager#setSchema} prior
+         *        to inserting a document of this {@code schemaType} into the AppSearch index using
+         *        {@code AppSearchManager#putDocuments}. Otherwise, the document will be
+         *        rejected by {@code AppSearchManager#putDocuments}.
+         */
+        //TODO(b/157082794) Linkify AppSearchManager once that API is public.
+        @SuppressWarnings("unchecked")
+        public Builder(@NonNull String uri, @NonNull String schemaType) {
+            Preconditions.checkNotNull(uri);
+            Preconditions.checkNotNull(schemaType);
+            mBuilderTypeInstance = (BuilderType) this;
+            mBundle.putString(GenericDocument.URI_FIELD, uri);
+            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+            mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+            // Set current timestamp for creation timestamp by default.
+            mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
+                    System.currentTimeMillis());
+            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+            mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
+            mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+        }
+
+        /**
+         * Set the app-defined namespace this Document resides in. No special values  are
+         * reserved or understood by the infrastructure. URIs are unique within a namespace. The
+         * number of namespaces per app should be kept small for efficiency reasons.
+         */
+        @NonNull
+        public BuilderType setNamespace(@NonNull String namespace) {
+            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets the score of the {@link GenericDocument}.
+         *
+         * <p>The score is a query-independent measure of the document's quality, relative to
+         * other {@link GenericDocument}s of the same type.
+         *
+         * @throws IllegalArgumentException If the provided value is negative.
+         */
+        @NonNull
+        public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            if (score < 0) {
+                throw new IllegalArgumentException("Document score cannot be negative.");
+            }
+            mBundle.putInt(GenericDocument.SCORE_FIELD, score);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Set the creation timestamp in milliseconds of the {@link GenericDocument}. Should be
+         * set using a value obtained from the {@link System#currentTimeMillis()} time base.
+         */
+        @NonNull
+        public BuilderType setCreationTimestampMillis(long creationTimestampMillis) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
+                    creationTimestampMillis);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Set the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+         *
+         * <p>After this many milliseconds since the {@link #setCreationTimestampMillis creation
+         * timestamp}, the document is deleted.
+         *
+         * @param ttlMillis A non-negative duration in milliseconds.
+         * @throws IllegalArgumentException If the provided value is negative.
+         */
+        @NonNull
+        public BuilderType setTtlMillis(long ttlMillis) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            if (ttlMillis < 0) {
+                throw new IllegalArgumentException("Document ttlMillis cannot be negative.");
+            }
+            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code String} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code String} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull String... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code boolean} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code boolean} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code long} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code long} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull long... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code double} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code double} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull double... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code byte[]} for a property, replacing its previous values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code byte[]} of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@link GenericDocument} values for a property, replacing its
+         * previous values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@link GenericDocument} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull GenericDocument... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull String[] values)
+                throws IllegalArgumentException {
+            validateRepeatedPropertyLength(key, values.length);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The String at " + i + " is null.");
+                } else if (values[i].length() > MAX_STRING_LENGTH) {
+                    throw new IllegalArgumentException("The String at " + i + " length is: "
+                            + values[i].length()  + ", which exceeds length limit: "
+                            + MAX_STRING_LENGTH + ".");
+                }
+            }
+            mProperties.putStringArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull boolean[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putBooleanArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull double[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putDoubleArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull long[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putLongArray(key, values);
+        }
+
+        /**
+         * Converts and saves a byte[][] into {@link #mProperties}.
+         *
+         * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][]
+         * into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
+         */
+        private void putInPropertyBundle(@NonNull String key, @NonNull byte[][] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            ArrayList<Bundle> bundles = new ArrayList<>(values.length);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The byte[] at " + i + " is null.");
+                }
+                Bundle bundle = new Bundle();
+                bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]);
+                bundles.add(bundle);
+            }
+            mProperties.putParcelableArrayList(key, bundles);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            Bundle[] documentBundles = new Bundle[values.length];
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The document at " + i + " is null.");
+                }
+                documentBundles[i] = values[i].mBundle;
+            }
+            mProperties.putParcelableArray(key, documentBundles);
+        }
+
+        private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
+            if (length == 0) {
+                throw new IllegalArgumentException("The input array is empty.");
+            } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
+                throw new IllegalArgumentException(
+                        "Repeated property \"" + key + "\" has length " + length
+                                + ", which exceeds the limit of "
+                                + MAX_REPEATED_PROPERTY_LENGTH);
+            }
+        }
+
+        /** Builds the {@link GenericDocument} object. */
+        @NonNull
+        public GenericDocument build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new GenericDocument(mBundle);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
new file mode 100644
index 0000000..3c0e746
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+import android.util.ArraySet;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to retrieve documents by namespace and URI.
+ *
+ * @see AppSearchManager#getByUri
+ * @hide
+ */
+public final class GetByUriRequest {
+    private final String mNamespace;
+    private final Set<String> mUris;
+
+    GetByUriRequest(@NonNull String namespace, @NonNull Set<String> uris) {
+        mNamespace = namespace;
+        mUris = uris;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<String> getUris() {
+        return mUris;
+    }
+
+    /** Builder for {@link GetByUriRequest} objects. */
+    public static final class Builder {
+        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private final Set<String> mUris = new ArraySet<>();
+        private boolean mBuilt = false;
+
+        /**
+         * Sets which namespace these documents will be retrieved from.
+         *
+         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         */
+        @NonNull
+        public Builder setNamespace(@NonNull String namespace) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(namespace);
+            mNamespace = namespace;
+            return this;
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull String... uris) {
+            Preconditions.checkNotNull(uris);
+            return addUris(Arrays.asList(uris));
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull Collection<String> uris) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(uris);
+            mUris.addAll(uris);
+            return this;
+        }
+
+        /** Builds a new {@link GetByUriRequest}. */
+        @NonNull
+        public GetByUriRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new GetByUriRequest(mNamespace, mUris);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index c710a29..352a980 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -21,12 +21,14 @@
 
 parcelable AppSearchResult;
 parcelable AppSearchBatchResult;
+parcelable SearchResults;
 
 /** {@hide} */
 interface IAppSearchManager {
     /**
      * Sets the schema.
      *
+     * @param databaseName  The databaseName this document resides in.
      * @param schemaBundles List of AppSearchSchema bundles.
      * @param forceOverride Whether to apply the new schema even if it is incompatible. All
      *     incompatible documents will be deleted.
@@ -34,6 +36,7 @@
      *     The results of the call.
      */
     void setSchema(
+        in String databaseName,
         in List<Bundle> schemaBundles,
         boolean forceOverride,
         in AndroidFuture<AppSearchResult> callback);
@@ -41,7 +44,8 @@
     /**
      * Inserts documents into the index.
      *
-     * @param documentsBytes {@link List}&lt;byte[]&gt; of serialized DocumentProtos.
+     * @param databaseName  The name of the database where this document lives.
+     * @param documentBundes List of GenericDocument bundles.
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
      *     If the call fails to start, {@code callback} will be completed exceptionally. Otherwise,
@@ -49,30 +53,40 @@
      *     {@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;
      *     where the keys are document URIs, and the values are {@code null}.
      */
-    void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback);
+    void putDocuments(
+        in String databaseName,
+        in List<Bundle> documentBundles,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Retrieves documents from the index.
      *
+     * @param databaseName  The databaseName this document resides in.
+     * @param namespace    The namespace this document resides in.
      * @param uris The URIs of the documents to retrieve
      * @param callback
-     *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link byte[]}&gt;&gt;.
+     *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Bundle}&gt;&gt;.
      *     If the call fails to start, {@code callback} will be completed exceptionally. Otherwise,
      *     {@code callback} will be completed with an
-     *     {@link AppSearchBatchResult}&lt;{@link String}, {@link byte[]}&gt;
-     *     where the keys are document URIs, and the values are serialized Document protos.
+     *     {@link AppSearchBatchResult}&lt;{@link String}, {@link Bundle}&gt;
+     *     where the keys are document URIs, and the values are Document bundles.
      */
-    void getDocuments(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback);
+    void getDocuments(
+        in String databaseName,
+        in String namespace,
+        in List<String> uris,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Searches a document based on a given specifications.
      *
+     * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
-     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link byte[]}&gt;&gt;
-     *     Will be completed with a serialized {@link SearchResultsProto}.
+     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link SearchResults}&gt;&gt;
      */
     void query(
+        in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
         in AndroidFuture<AppSearchResult> callback);
@@ -80,6 +94,8 @@
     /**
      * Deletes documents by URI.
      *
+     * @param databaseName The databaseName the document is in.
+     * @param namespace    Namespace of the document to remove.
      * @param uris The URIs of the documents to delete
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
@@ -89,11 +105,16 @@
      *     where the keys are document URIs. If a document doesn't exist, it will be reported as a
      *     failure where the {@code throwable} is {@code null}.
      */
-    void delete(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback);
+    void delete(
+        in String databaseName,
+        in String namespace,
+        in List<String> uris,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Deletes documents by schema type.
      *
+     * @param databaseName The databaseName the document is in.
      * @param schemaTypes The schema types of the documents to delete
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
@@ -104,13 +125,16 @@
      *     failure where the {@code throwable} is {@code null}.
      */
     void deleteByTypes(
-        in List<String> schemaTypes, in AndroidFuture<AppSearchBatchResult> callback);
+        in String databaseName,
+        in List<String> schemaTypes,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Deletes all documents belonging to the calling app.
      *
+     * @param databaseName The databaseName to remove all documents from.
      * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link Void}&gt;&gt;.
      *     Will be completed with the result of the call.
      */
-    void deleteAll(in AndroidFuture<AppSearchResult> callback);
+    void deleteAll(in String databaseName, in AndroidFuture<AppSearchResult> callback);
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java b/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
deleted file mode 100644
index 5ce2960..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2020 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.app.appsearch;
-
-import android.annotation.NonNull;
-import android.util.Range;
-
-import com.google.android.icing.proto.SnippetMatchProto;
-
-/**
- * Snippet: It refers to a substring of text from the content of document that is returned as a
- * part of search result.
- * This class represents a match objects for any Snippets that might be present in
- * {@link SearchResults} from query. Using this class user can get the full text, exact matches and
- * Snippets of document content for a given match.
- *
- * <p>Class Example 1:
- * A document contains following text in property subject:
- * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
- *
- * <p>If the queryExpression is "foo".
- *
- * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
- * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another nonsense
- * word that’s used a lot is bar."
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
- * <p>{@link MatchInfo#getExactMatch()} returns "foo"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [29, 41]
- * <p>{@link MatchInfo#getSnippet()} returns "is foo. Another"
- * <p>
- * <p>Class Example 2:
- * A document contains a property name sender which contains 2 property names name and email, so
- * we will have 2 property paths: {@code sender.name} and {@code sender.email}.
- * <p> Let {@code sender.name = "Test Name Jr."} and {@code sender.email = "TestNameJr@gmail.com"}
- *
- * <p>If the queryExpression is "Test". We will have 2 matches.
- *
- * <p> Match-1
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
- * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
- * <p>{@link MatchInfo#getExactMatch()} returns "Test"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
- * <p>{@link MatchInfo#getSnippet()} returns "Test Name Jr."
- * <p> Match-2
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
- * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
- * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
- * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
- * @hide
- */
-// TODO(sidchhabra): Capture real snippet after integration with icingLib.
-public final class MatchInfo {
-
-    private final String mPropertyPath;
-    private final SnippetMatchProto mSnippetMatch;
-    private final AppSearchDocument mDocument;
-    /**
-     * List of content with same property path in a document when there are multiple matches in
-     * repeated sections.
-     */
-    private final String[] mValues;
-
-    /** @hide */
-    public MatchInfo(@NonNull String propertyPath, @NonNull SnippetMatchProto snippetMatch,
-            @NonNull AppSearchDocument document) {
-        mPropertyPath = propertyPath;
-        mSnippetMatch = snippetMatch;
-        mDocument = document;
-        // In IcingLib snippeting is available for only 3 data types i.e String, double and long,
-        // so we need to check which of these three are requested.
-        // TODO (sidchhabra): getPropertyStringArray takes property name, handle for property path.
-        String[] values = mDocument.getPropertyStringArray(propertyPath);
-        if (values == null) {
-            values = doubleToString(mDocument.getPropertyDoubleArray(propertyPath));
-        }
-        if (values == null) {
-            values = longToString(mDocument.getPropertyLongArray(propertyPath));
-        }
-        if (values == null) {
-            throw new IllegalStateException("No content found for requested property path!");
-        }
-        mValues = values;
-    }
-
-    /**
-     * Gets the property path corresponding to the given entry.
-     * <p>Property Path: '.' - delimited sequence of property names indicating which property in
-     * the Document these snippets correspond to.
-     * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
-     * For class example 1 this returns "subject"
-     */
-    @NonNull
-    public String getPropertyPath() {
-        return mPropertyPath;
-    }
-
-    /**
-     * Gets the full text corresponding to the given entry.
-     * <p>For class example this returns "A commonly used fake word is foo. Another nonsense word
-     * that’s used a lot is bar."
-     */
-    @NonNull
-    public String getFullText() {
-        return mValues[mSnippetMatch.getValuesIndex()];
-    }
-
-    /**
-     * Gets the exact match range corresponding to the given entry.
-     * <p>For class example 1 this returns [29, 32]
-     */
-    @NonNull
-    public Range getExactMatchPosition() {
-        return new Range(mSnippetMatch.getExactMatchPosition(),
-                mSnippetMatch.getExactMatchPosition() + mSnippetMatch.getExactMatchBytes());
-    }
-
-    /**
-     * Gets the exact match corresponding to the given entry.
-     * <p>For class example 1 this returns "foo"
-     */
-    @NonNull
-    public CharSequence getExactMatch() {
-        return getSubstring(getExactMatchPosition());
-    }
-
-    /**
-     * Gets the snippet range corresponding to the given entry.
-     * <p>For class example 1 this returns [29, 41]
-     */
-    @NonNull
-    public Range getSnippetPosition() {
-        return new Range(mSnippetMatch.getWindowPosition(),
-                mSnippetMatch.getWindowPosition() + mSnippetMatch.getWindowBytes());
-    }
-
-    /**
-     * Gets the snippet corresponding to the given entry.
-     * <p>Snippet - Provides a subset of the content to display. The
-     * length of this content can be changed {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
-     * Windowing is centered around the middle of the matched token with content on either side
-     * clipped to token boundaries.
-     * <p>For class example 1 this returns "foo. Another"
-     */
-    @NonNull
-    public CharSequence getSnippet() {
-        return getSubstring(getSnippetPosition());
-    }
-
-    private CharSequence getSubstring(Range range) {
-        return getFullText()
-                .substring((int) range.getLower(), (int) range.getUpper());
-    }
-
-    /** Utility method to convert double[] to String[] */
-    private String[] doubleToString(double[] values) {
-        //TODO(sidchhabra): Implement the method.
-        return null;
-    }
-
-    /** Utility method to convert long[] to String[] */
-    private String[] longToString(long[] values) {
-        //TODO(sidchhabra): Implement the method.
-        return null;
-    }
-}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
new file mode 100644
index 0000000..7e97542
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Encapsulates a request to index a document into an {@link AppSearchManager} database.
+ *
+ * @see AppSearchManager#putDocuments
+ * @hide
+ */
+public final class PutDocumentsRequest {
+    private final List<GenericDocument> mDocuments;
+
+    PutDocumentsRequest(List<GenericDocument> documents) {
+        mDocuments = documents;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public List<GenericDocument> getDocuments() {
+        return mDocuments;
+    }
+
+    /** Builder for {@link PutDocumentsRequest} objects. */
+    public static final class Builder {
+        private final List<GenericDocument> mDocuments = new ArrayList<>();
+        private boolean mBuilt = false;
+
+        /** Adds one or more documents to the request. */
+        @NonNull
+        public Builder addGenericDocument(@NonNull GenericDocument... documents) {
+            Preconditions.checkNotNull(documents);
+            return addGenericDocument(Arrays.asList(documents));
+        }
+
+        /** Adds one or more documents to the request. */
+        @NonNull
+        public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(documents);
+            mDocuments.addAll(documents);
+            return this;
+        }
+
+        /** Builds a new {@link PutDocumentsRequest}. */
+        @NonNull
+        public PutDocumentsRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new PutDocumentsRequest(mDocuments);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
new file mode 100644
index 0000000..a047041
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+import android.util.ArraySet;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to remove documents by namespace and URI.
+ *
+ * @see AppSearchManager#removeByUri
+ * @hide
+ */
+public final class RemoveByUriRequest {
+    private final String mNamespace;
+    private final Set<String> mUris;
+
+    RemoveByUriRequest(String namespace, Set<String> uris) {
+        mNamespace = namespace;
+        mUris = uris;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<String> getUris() {
+        return mUris;
+    }
+
+    /** Builder for {@link RemoveByUriRequest} objects. */
+    public static final class Builder {
+        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private final Set<String> mUris = new ArraySet<>();
+        private boolean mBuilt = false;
+
+        /**
+         * Sets which namespace these documents will be removed from.
+         *
+         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         */
+        @NonNull
+        public Builder setNamespace(@NonNull String namespace) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(namespace);
+            mNamespace = namespace;
+            return this;
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull String... uris) {
+            Preconditions.checkNotNull(uris);
+            return addUris(Arrays.asList(uris));
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull Collection<String> uris) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(uris);
+            mUris.addAll(uris);
+            return this;
+        }
+
+        /** Builds a new {@link RemoveByUriRequest}. */
+        @NonNull
+        public RemoveByUriRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new RemoveByUriRequest(mNamespace, mUris);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
new file mode 100644
index 0000000..758280b
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents one of the results obtained from the query.
+ *
+ * <p>It contains the document which matched, information about which section(s) in the document
+ * matched, and snippet information containing textual summaries of the document's match(es).
+ * @hide
+ */
+public final class SearchResult {
+    /** @hide */
+    
+    public static final String DOCUMENT_FIELD = "document";
+
+    /** @hide */
+    
+    public static final String MATCHES_FIELD = "matches";
+
+    @NonNull
+    private final Bundle mBundle;
+
+    @NonNull
+    private final Bundle mDocumentBundle;
+
+    @Nullable
+    private GenericDocument mDocument;
+
+    @Nullable
+    private final List<Bundle> mMatchBundles;
+
+    /**
+     * Contains a list of Snippets that matched the request. Only populated when requested in
+     * both {@link SearchSpec.Builder#setSnippetCount(int)}
+     * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}.
+     *
+     * @see #getMatches()
+     */
+    @Nullable
+    private List<MatchInfo> mMatches;
+
+    /** @hide */
+    
+    public SearchResult(@NonNull Bundle bundle) {
+        mBundle = Preconditions.checkNotNull(bundle);
+        mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD));
+        mMatchBundles = bundle.getParcelableArrayList(MATCHES_FIELD);
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /**
+     * Contains the matching {@link GenericDocument}.
+     * @return Document object which matched the query.
+     */
+    @NonNull
+    public GenericDocument getDocument() {
+        if (mDocument == null) {
+            mDocument = new GenericDocument(mDocumentBundle);
+        }
+        return mDocument;
+    }
+
+    /**
+     * Contains a list of Snippets that matched the request. Only populated when requested in
+     * both {@link SearchSpec.Builder#setSnippetCount(int)}
+     * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}.
+     *
+     * @return  List of matches based on {@link SearchSpec}, if snippeting is disabled and this
+     * method is called it will return {@code null}. Users can also restrict snippet population
+     * using {@link SearchSpec.Builder#setSnippetCount} and
+     * {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}, for all results after that
+     * value this method will return {@code null}.
+     */
+    @Nullable
+    public List<MatchInfo> getMatches() {
+        if (mMatchBundles != null && mMatches == null) {
+            mMatches = new ArrayList<>(mMatchBundles.size());
+            for (int i = 0; i < mMatchBundles.size(); i++) {
+                MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i));
+                mMatches.add(matchInfo);
+            }
+        }
+        return mMatches;
+    }
+
+    /**
+     * Snippet: It refers to a substring of text from the content of document that is returned as a
+     * part of search result.
+     * This class represents a match objects for any Snippets that might be present in
+     * {@link SearchResults} from query. Using this class user can get the full text, exact matches
+     * and Snippets of document content for a given match.
+     *
+     * <p>Class Example 1:
+     * A document contains following text in property subject:
+     * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
+     *
+     * <p>If the queryExpression is "foo".
+     *
+     * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
+     * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
+     * nonsense word that’s used a lot is bar."
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+     * <p>{@link MatchInfo#getExactMatch()} returns "foo"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
+     * <p>{@link MatchInfo#getSnippet()} returns "is foo."
+     * <p>
+     * <p>Class Example 2:
+     * A document contains a property name sender which contains 2 property names name and email, so
+     * we will have 2 property paths: {@code sender.name} and {@code sender.email}.
+     * <p>Let {@code sender.name = "Test Name Jr."} and
+     * {@code sender.email = "TestNameJr@gmail.com"}
+     *
+     * <p>If the queryExpression is "Test". We will have 2 matches.
+     *
+     * <p> Match-1
+     * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
+     * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+     * <p>{@link MatchInfo#getExactMatch()} returns "Test"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+     * <p>{@link MatchInfo#getSnippet()} returns "Test Name"
+     * <p> Match-2
+     * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
+     * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
+     */
+    public static final class MatchInfo {
+        /**
+         * The path of the matching snippet property.
+         * @hide
+         */
+        
+        public static final String PROPERTY_PATH_FIELD = "propertyPath";
+
+        /**
+         * The index of matching value in its property. A property may have multiple values. This
+         * index indicates which value is the match.
+         * @hide
+         */
+        
+        public static final String VALUES_INDEX_FIELD = "valuesIndex";
+
+        /** @hide */
+        
+        public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
+
+        /** @hide */
+        
+        public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
+
+        /** @hide */
+        
+        public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
+
+        /** @hide */
+        
+        public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
+
+        private final String mFullText;
+        private final String mPropertyPath;
+        private final Bundle mBundle;
+        private MatchRange mExactMatchRange;
+        private MatchRange mWindowRange;
+
+        MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+            Preconditions.checkNotNull(document);
+            mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
+            mFullText = getPropertyValues(
+                    document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
+        }
+
+        /**
+         * Gets the property path corresponding to the given entry.
+         * <p>Property Path: '.' - delimited sequence of property names indicating which property in
+         * the Document these snippets correspond to.
+         * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
+         * For class example 1 this returns "subject"
+         */
+        @NonNull
+        public String getPropertyPath() {
+            return mPropertyPath;
+        }
+
+        /**
+         * Gets the full text corresponding to the given entry.
+         * <p>For class example this returns "A commonly used fake word is foo. Another nonsense
+         * word that's used a lot is bar."
+         */
+        @NonNull
+        public String getFullText() {
+            return mFullText;
+        }
+
+        /**
+         * Gets the exact {@link MatchRange} corresponding to the given entry.
+         * <p>For class example 1 this returns [29, 32]
+         */
+        @NonNull
+        public MatchRange getExactMatchPosition() {
+            if (mExactMatchRange == null) {
+                mExactMatchRange = new MatchRange(
+                        mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
+                        mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
+            }
+            return mExactMatchRange;
+        }
+
+        /**
+         * Gets the  {@link MatchRange} corresponding to the given entry.
+         * <p>For class example 1 this returns "foo"
+         */
+        @NonNull
+        public CharSequence getExactMatch() {
+            return getSubstring(getExactMatchPosition());
+        }
+
+        /**
+         * Gets the snippet {@link MatchRange} corresponding to the given entry.
+         * <p>Only populated when set maxSnippetSize > 0 in
+         * {@link SearchSpec.Builder#setMaxSnippetSize}.
+         * <p>For class example 1 this returns [29, 41].
+         */
+        @NonNull
+        public MatchRange getSnippetPosition() {
+            if (mWindowRange == null) {
+                mWindowRange = new MatchRange(
+                        mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
+                        mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
+            }
+            return mWindowRange;
+        }
+
+        /**
+         * Gets the snippet corresponding to the given entry.
+         * <p>Snippet - Provides a subset of the content to display. Only populated when requested
+         * maxSnippetSize > 0. The size of this content can be changed by
+         * {@link SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of
+         * the matched token with content on either side clipped to token boundaries.
+         * <p>For class example 1 this returns "foo. Another"
+         */
+        @NonNull
+        public CharSequence getSnippet() {
+            return getSubstring(getSnippetPosition());
+        }
+
+        private CharSequence getSubstring(MatchRange range) {
+            return getFullText().substring(range.getStart(), range.getEnd());
+        }
+
+        /** Extracts the matching string from the document. */
+        private static String getPropertyValues(
+                GenericDocument document, String propertyName, int valueIndex) {
+            // In IcingLib snippeting is available for only 3 data types i.e String, double and
+            // long, so we need to check which of these three are requested.
+            // TODO (tytytyww): getPropertyStringArray takes property name, handle for property
+            //  path.
+            // TODO (tytytyww): support double[] and long[].
+            String[] values = document.getPropertyStringArray(propertyName);
+            if (values == null) {
+                throw new IllegalStateException("No content found for requested property path!");
+            }
+            return values[valueIndex];
+        }
+    }
+
+    /**
+     * Class providing the position range of matching information.
+     *
+     * <p> All ranges are finite, and the left side of the range is always {@code <=} the right
+     * side of the range.
+     *
+     * <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
+     *
+     */
+    public static final class MatchRange {
+        private final int mEnd;
+        private final int mStart;
+
+        /**
+         * Creates a new immutable range.
+         * <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
+         * must be lesser or equal to {@code end}.
+         *
+         * @param start The start point (inclusive)
+         * @param end The end point (exclusive)
+         * @hide
+         */
+        
+        public MatchRange(int start, int end) {
+            if (start > end) {
+                throw new IllegalArgumentException("Start point must be less than or equal to "
+                        + "end point");
+            }
+            mStart = start;
+            mEnd = end;
+        }
+
+        /** Gets the start point (inclusive). */
+        public int getStart() {
+            return mStart;
+        }
+
+        /** Gets the end point (exclusive). */
+        public int getEnd() {
+            return mEnd;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof MatchRange)) {
+                return false;
+            }
+            MatchRange otherMatchRange = (MatchRange) other;
+            return this.getStart() == otherMatchRange.getStart()
+                    && this.getEnd() == otherMatchRange.getEnd();
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "MatchRange { start: " + mStart + " , end: " + mEnd + "}";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mStart, mEnd);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 7287fe6..9f37625 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -17,112 +17,62 @@
 package android.app.appsearch;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.google.android.icing.proto.SearchResultProto;
-import com.google.android.icing.proto.SnippetMatchProto;
-import com.google.android.icing.proto.SnippetProto;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /**
- * SearchResults are a list of results that are returned from a query. Each result from this
- * list contains a document and may contain other fields like snippets based on request.
- * This iterator class is not thread safe.
+ * Structure for transmitting a page of search results across binder.
  * @hide
  */
-public final class SearchResults implements Iterator<SearchResults.Result> {
+public final class SearchResults implements Parcelable {
+    final List<SearchResult> mResults;
+    final long mNextPageToken;
 
-    private final SearchResultProto mSearchResultProto;
-    private int mNextIdx;
+    public SearchResults(@NonNull List<SearchResult> results, long nextPageToken) {
+        mResults = results;
+        mNextPageToken = nextPageToken;
+    }
 
-    /** @hide */
-    public SearchResults(SearchResultProto searchResultProto) {
-        mSearchResultProto = searchResultProto;
+    private SearchResults(@NonNull Parcel in) {
+        List<Bundle> resultBundles = in.readArrayList(/*loader=*/ null);
+        mResults = new ArrayList<>(resultBundles.size());
+        for (int i = 0; i < resultBundles.size(); i++) {
+            SearchResult searchResult = new SearchResult(resultBundles.get(i));
+            mResults.add(searchResult);
+        }
+        mNextPageToken = in.readLong();
     }
 
     @Override
-    public boolean hasNext() {
-        return mNextIdx < mSearchResultProto.getResultsCount();
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        List<Bundle> resultBundles = new ArrayList<>(mResults.size());
+        for (int i = 0; i < mResults.size(); i++) {
+            resultBundles.add(mResults.get(i).getBundle());
+        }
+        dest.writeList(resultBundles);
+        dest.writeLong(mNextPageToken);
     }
 
-    @NonNull
     @Override
-    public Result next() {
-        if (!hasNext()) {
-            throw new NoSuchElementException();
-        }
-        Result result = new Result(mSearchResultProto.getResults(mNextIdx));
-        mNextIdx++;
-        return result;
+    public int describeContents() {
+        return 0;
     }
 
-
-
-    /**
-     * This class represents the result obtained from the query. It will contain the document which
-     * which matched the specified query string and specifications.
-     * @hide
-     */
-    public static final class Result {
-        private final SearchResultProto.ResultProto mResultProto;
-
-        @Nullable
-        private AppSearchDocument mDocument;
-
-        private Result(SearchResultProto.ResultProto resultProto) {
-            mResultProto = resultProto;
-        }
-
-        /**
-         * Contains the matching {@link AppSearchDocument}.
-         * @return Document object which matched the query.
-         * @hide
-         */
+    public static final Creator<SearchResults> CREATOR = new Creator<SearchResults>() {
         @NonNull
-        public AppSearchDocument getDocument() {
-            if (mDocument == null) {
-                mDocument = new AppSearchDocument(mResultProto.getDocument());
-            }
-            return mDocument;
+        @Override
+        public SearchResults createFromParcel(@NonNull Parcel in) {
+            return new SearchResults(in);
         }
 
-        /**
-         * Contains a list of Snippets that matched the request. Only populated when requested in
-         * {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
-         * @return  List of matches based on {@link SearchSpec}, if snippeting is disabled and this
-         * method is called it will return {@code null}. Users can also restrict snippet population
-         * using {@link SearchSpec.Builder#setNumToSnippet} and
-         * {@link SearchSpec.Builder#setNumMatchesPerProperty}, for all results after that value
-         * this method will return {@code null}.
-         * @hide
-         */
-        // TODO(sidchhabra): Replace Document with proper constructor.
-        @Nullable
-        public List<MatchInfo> getMatchInfo() {
-            if (!mResultProto.hasSnippet()) {
-                return null;
-            }
-            AppSearchDocument document = getDocument();
-            List<MatchInfo> matchList = new ArrayList<>();
-            for (Iterator entryProtoIterator = mResultProto.getSnippet()
-                    .getEntriesList().iterator(); entryProtoIterator.hasNext(); ) {
-                SnippetProto.EntryProto entry = (SnippetProto.EntryProto) entryProtoIterator.next();
-                for (Iterator snippetMatchProtoIterator = entry.getSnippetMatchesList().iterator();
-                        snippetMatchProtoIterator.hasNext(); ) {
-                    matchList.add(new MatchInfo(entry.getPropertyName(),
-                            (SnippetMatchProto) snippetMatchProtoIterator.next(), document));
-                }
-            }
-            return matchList;
+        @NonNull
+        @Override
+        public SearchResults[] newArray(int size) {
+            return new SearchResults[size];
         }
-    }
-
-    @Override
-    public String toString() {
-        return mSearchResultProto.toString();
-    }
+    };
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
index 817c3ef..c871905 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -249,8 +249,10 @@
         /**
          * Only the first {@code snippetCount} documents based on the ranking strategy
          * will have snippet information provided.
-         * <p>If set to 0 (default), snippeting is disabled and
-         * {@link SearchResults.Result#getMatches} will return {@code null} for that result.
+         *
+         * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will
+         * return {@code null} for that result.
+         *
          * <p>The value should be set in range[0, 10k].
          */
         @NonNull
@@ -264,8 +266,10 @@
         /**
          * Only the first {@code matchesCountPerProperty} matches for a every property of
          * {@link GenericDocument} will contain snippet information.
-         * <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatches}
-         * will return {@code null} for that result.
+         *
+         * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return
+         * {@code null} for that result.
+         *
          * <p>The value should be set in range[0, 10k].
          */
         @NonNull
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
new file mode 100644
index 0000000..b2e9d46
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import android.annotation.NonNull;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to update the schema of an {@link AppSearchManager} database.
+ *
+ * @see AppSearchManager#setSchema
+ * @hide
+ */
+public final class SetSchemaRequest {
+    private final Set<AppSearchSchema> mSchemas;
+    private final boolean mForceOverride;
+
+    SetSchemaRequest(Set<AppSearchSchema> schemas, boolean forceOverride) {
+        mSchemas = schemas;
+        mForceOverride = forceOverride;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<AppSearchSchema> getSchemas() {
+        return mSchemas;
+    }
+
+    /** @hide */
+    
+    public boolean isForceOverride() {
+        return mForceOverride;
+    }
+
+    /** Builder for {@link SetSchemaRequest} objects. */
+    public static final class Builder {
+        private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
+        private boolean mForceOverride = false;
+        private boolean mBuilt = false;
+
+        /** Adds one or more types to the schema. */
+        @NonNull
+        public Builder addSchema(@NonNull AppSearchSchema... schemas) {
+            Preconditions.checkNotNull(schemas);
+            return addSchema(Arrays.asList(schemas));
+        }
+
+        /** Adds one or more types to the schema. */
+        @NonNull
+        public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemas);
+            mSchemas.addAll(schemas);
+            return this;
+        }
+
+        /**
+         * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
+         * follow the new schema.
+         *
+         * <p>By default, this is {@code false} and schema incompatibility causes the
+         * {@link AppSearchManager#setSchema} call to fail.
+         *
+         * @see AppSearchManager#setSchema
+         */
+        @NonNull
+        public Builder setForceOverride(boolean forceOverride) {
+            mForceOverride = forceOverride;
+            return this;
+        }
+
+        /** Builds a new {@link SetSchemaRequest}. */
+        @NonNull
+        public SetSchemaRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new SetSchemaRequest(mSchemas, mForceOverride);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
index 00f6e75..d490469 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
@@ -54,6 +54,10 @@
         mResultCode = resultCode;
     }
 
+    public @AppSearchResult.ResultCode int getResultCode() {
+        return mResultCode;
+    }
+
     /**
      * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
      */
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 4bc0c39..7cd6ee2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -17,10 +17,12 @@
 
 import android.annotation.NonNull;
 import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchDocument;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
 import android.app.appsearch.IAppSearchManager;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
@@ -32,7 +34,9 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localbackend.AppSearchImpl;
+import com.android.server.appsearch.external.localbackend.converter.GenericDocumentToProtoConverter;
 import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter;
+import com.android.server.appsearch.external.localbackend.converter.SearchResultToProtoConverter;
 import com.android.server.appsearch.external.localbackend.converter.SearchSpecToProtoConverter;
 
 import com.google.android.icing.proto.DocumentProto;
@@ -49,6 +53,7 @@
  */
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
+    private static final char CALLING_NAME_DATABASE_DELIMITER = '$';
 
     public AppSearchManagerService(Context context) {
         super(context);
@@ -62,6 +67,7 @@
     private class Stub extends IAppSearchManager.Stub {
         @Override
         public void setSchema(
+                @NonNull String databaseName,
                 @NonNull List<Bundle> schemaBundles,
                 boolean forceOverride,
                 @NonNull AndroidFuture<AppSearchResult> callback) {
@@ -78,9 +84,9 @@
                     schemaProtoBuilder.addTypes(schemaTypeProto);
                 }
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 impl.setSchema(databaseName, schemaProtoBuilder.build(), forceOverride);
-                callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null));
+                callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null));
             } catch (Throwable t) {
                 callback.complete(throwableToFailedResult(t));
             } finally {
@@ -90,24 +96,25 @@
 
         @Override
         public void putDocuments(
-                @NonNull List documentsBytes,
+                @NonNull String databaseName,
+                @NonNull List<Bundle> documentBundles,
                 @NonNull AndroidFuture<AppSearchBatchResult> callback) {
-            Preconditions.checkNotNull(documentsBytes);
+            Preconditions.checkNotNull(documentBundles);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                for (int i = 0; i < documentsBytes.size(); i++) {
-                    byte[] documentBytes = (byte[]) documentsBytes.get(i);
-                    DocumentProto document = DocumentProto.parseFrom(documentBytes);
+                for (int i = 0; i < documentBundles.size(); i++) {
+                    GenericDocument document = new GenericDocument(documentBundles.get(i));
+                    DocumentProto documentProto = GenericDocumentToProtoConverter.convert(document);
                     try {
-                        impl.putDocument(databaseName, document);
-                        resultBuilder.setSuccess(document.getUri(), /*value=*/ null);
+                        impl.putDocument(databaseName, documentProto);
+                        resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
                     }
@@ -121,7 +128,7 @@
         }
 
         @Override
-        public void getDocuments(
+        public void getDocuments(@NonNull String databaseName, @NonNull String namespace,
                 @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(uris);
             Preconditions.checkNotNull(callback);
@@ -130,19 +137,21 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
-                AppSearchBatchResult.Builder<String, byte[]> resultBuilder =
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
+                AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        DocumentProto document = impl.getDocument(
-                                databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
-                        if (document == null) {
+                        DocumentProto documentProto = impl.getDocument(
+                                databaseName, namespace, uri);
+                        if (documentProto == null) {
                             resultBuilder.setFailure(
                                     uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null);
                         } else {
-                            resultBuilder.setSuccess(uri, document.toByteArray());
+                            GenericDocument genericDocument =
+                                    GenericDocumentToProtoConverter.convert(documentProto);
+                            resultBuilder.setSuccess(uri, genericDocument.getBundle());
                         }
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -159,6 +168,7 @@
         // TODO(sidchhabra): Do this in a threadpool.
         @Override
         public void query(
+                @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
                 @NonNull AndroidFuture<AppSearchResult> callback) {
@@ -175,15 +185,18 @@
                 searchSpecProto = searchSpecProto.toBuilder()
                         .setQuery(queryExpression).build();
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 // TODO(adorokhine): handle pagination
                 SearchResultProto searchResultProto = impl.query(
                         databaseName,
                         searchSpecProto,
                         SearchSpecToProtoConverter.toResultSpecProto(searchSpec),
                         SearchSpecToProtoConverter.toScoringSpecProto(searchSpec));
-                callback.complete(
-                        AppSearchResult.newSuccessfulResult(searchResultProto.toByteArray()));
+                List<SearchResult> searchResultList =
+                        SearchResultToProtoConverter.convert(searchResultProto);
+                SearchResults searchResults =
+                        new SearchResults(searchResultList, searchResultProto.getNextPageToken());
+                callback.complete(AppSearchResult.newSuccessfulResult(searchResults));
             } catch (Throwable t) {
                 callback.complete(throwableToFailedResult(t));
             } finally {
@@ -192,7 +205,8 @@
         }
 
         @Override
-        public void delete(List<String> uris, AndroidFuture<AppSearchBatchResult> callback) {
+        public void delete(@NonNull String databaseName, @NonNull String namespace,
+                List<String> uris, AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(uris);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
@@ -200,14 +214,14 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
-                        resultBuilder.setSuccess(uri, /*value= */null);
+                        impl.remove(databaseName, namespace, uri);
+                        resultBuilder.setSuccess(uri, /*result= */null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
                     }
@@ -221,7 +235,7 @@
         }
 
         @Override
-        public void deleteByTypes(
+        public void deleteByTypes(@NonNull String databaseName,
                 List<String> schemaTypes, AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkNotNull(callback);
@@ -230,14 +244,14 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < schemaTypes.size(); i++) {
                     String schemaType = schemaTypes.get(i);
                     try {
                         impl.removeByType(databaseName, schemaType);
-                        resultBuilder.setSuccess(schemaType, /*value=*/ null);
+                        resultBuilder.setSuccess(schemaType, /*result=*/ null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(schemaType, throwableToFailedResult(t));
                     }
@@ -251,14 +265,15 @@
         }
 
         @Override
-        public void deleteAll(@NonNull AndroidFuture<AppSearchResult> callback) {
+        public void deleteAll(@NonNull String databaseName,
+                @NonNull AndroidFuture<AppSearchResult> callback) {
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 impl.removeAll(databaseName);
                 callback.complete(AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
@@ -269,13 +284,13 @@
         }
 
         /**
-         * Returns a unique database name for the given uid.
+         * Rewrites the database name by adding a prefix of unique name for the given uid.
          *
          * <p>The current implementation returns the package name of the app with this uid in a
          * format like {@code com.example.package} or {@code com.example.sharedname:5678}.
          */
         @NonNull
-        private String makeDatabaseName(int callingUid) {
+        private String rewriteDatabaseNameWithUid(String databaseName, int callingUid) {
             // For regular apps, this call will return the package name. If callingUid is an
             // android:sharedUserId, this value may be another type of name and have a :uid suffix.
             String callingUidName = getContext().getPackageManager().getNameForUid(callingUid);
@@ -284,7 +299,7 @@
                 throw new IllegalStateException(
                         "Failed to look up package name for uid " + callingUid);
             }
-            return callingUidName;
+            return callingUidName + CALLING_NAME_DATABASE_DELIMITER + databaseName;
         }
 
         private <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index c1e6b0f..60f7005 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -69,9 +69,7 @@
     private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(context, userId);
-        AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir);
-        appSearchImpl.initialize();
-        return appSearchImpl;
+        return AppSearchImpl.create(appSearchDir);
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
index 462f458..642378d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
@@ -18,7 +18,6 @@
 
 import android.util.Log;
 
-import android.annotation.AnyThread;
 import com.android.internal.annotations.GuardedBy;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +26,7 @@
 import android.annotation.WorkerThread;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.exceptions.AppSearchException;
+import com.android.internal.util.Preconditions;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByNamespaceResultProto;
@@ -58,7 +58,6 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -66,8 +65,7 @@
  * Manages interaction with the native IcingSearchEngine and other components to implement AppSearch
  * functionality.
  *
- * <p>Callers should call {@link #initialize} before using the AppSearchImpl instance. Never create
- * two instances using the same folder.
+ * <p>Never create two instances using the same folder.
  *
  * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
  * are physically saved together in {@link IcingSearchEngine}, but logically isolated:
@@ -106,9 +104,7 @@
     static final int CHECK_OPTIMIZE_INTERVAL = 100;
 
     private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
-    private final CountDownLatch mInitCompleteLatch = new CountDownLatch(1);
-    private final File mIcingDir;
-    private IcingSearchEngine mIcingSearchEngine;
+    private final IcingSearchEngine mIcingSearchEngine;
 
     // The map contains schemaTypes and namespaces for all database. All values in the map have
     // been already added database name prefix.
@@ -121,33 +117,24 @@
      */
     private int mOptimizeIntervalCount = 0;
 
-    /** Creates an instance of {@link AppSearchImpl} which writes data to the given folder. */
-    @AnyThread
-    public AppSearchImpl(@NonNull File icingDir) {
-        mIcingDir = icingDir;
+    /**
+     * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
+     * folder.
+     */
+    @NonNull
+    public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
+        Preconditions.checkNotNull(icingDir);
+        return new AppSearchImpl(icingDir);
     }
 
-    /**
-     * Initializes the underlying IcingSearchEngine.
-     *
-     * <p>This method belongs to mutate group.
-     *
-     * @throws AppSearchException on IcingSearchEngine error.
-     */
-    public void initialize() throws AppSearchException {
-        if (isInitialized()) {
-            return;
-        }
+    private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
         boolean isReset = false;
         mReadWriteLock.writeLock().lock();
         try {
-        // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
-        // than once. It's unnecessary and can be a costly operation.
-            if (isInitialized()) {
-                return;
-            }
+            // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
+            // than once. It's unnecessary and can be a costly operation.
             IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
-                    .setBaseDir(mIcingDir.getAbsolutePath()).build();
+                    .setBaseDir(icingDir.getAbsolutePath()).build();
             mIcingSearchEngine = new IcingSearchEngine(options);
 
             InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize();
@@ -170,7 +157,8 @@
             for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
                 addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
             }
-            mInitCompleteLatch.countDown();
+            // TODO(b/155939114): It's possible to optimize after init, which would reduce the time
+            //   to when we're able to serve queries. Consider moving this optimize call out.
             if (!isReset) {
                 checkForOptimize(/* force= */ true);
             }
@@ -179,12 +167,6 @@
         }
     }
 
-    /** Checks if the internal state of {@link AppSearchImpl} has been initialized. */
-    @AnyThread
-    public boolean isInitialized() {
-        return mInitCompleteLatch.getCount() == 0;
-    }
-
     /**
      * Updates the AppSearch schema for this app.
      *
@@ -195,12 +177,9 @@
      * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
      *                      which do not comply with the new schema will be deleted.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void setSchema(@NonNull String databaseName, @NonNull SchemaProto origSchema,
-            boolean forceOverride) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            boolean forceOverride) throws AppSearchException {
         SchemaProto schemaProto = getSchemaProto();
 
         SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
@@ -212,10 +191,32 @@
         SetSchemaResultProto setSchemaResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            setSchemaResultProto = mIcingSearchEngine.setSchema(existingSchemaBuilder.build(),
-                    forceOverride);
-            checkSuccess(setSchemaResultProto.getStatus());
+            // Apply schema
+            setSchemaResultProto =
+                    mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
+
+            // Determine whether it succeeded.
+            try {
+                checkSuccess(setSchemaResultProto.getStatus());
+            } catch (AppSearchException e) {
+                // Improve the error message by merging in information about incompatible types.
+                if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
+                        || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
+                    String newMessage = e.getMessage()
+                            + "\n  Deleted types: "
+                            + setSchemaResultProto.getDeletedSchemaTypesList()
+                            + "\n  Incompatible types: "
+                            + setSchemaResultProto.getIncompatibleSchemaTypesList();
+                    throw new AppSearchException(e.getResultCode(), newMessage, e.getCause());
+                } else {
+                    throw e;
+                }
+            }
+
+            // Update derived data structures.
             mSchemaMap.put(databaseName, newTypeNames);
+
+            // Determine whether to schedule an immediate optimize.
             if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
                     || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
                     && forceOverride)) {
@@ -237,12 +238,9 @@
      * @param databaseName The databaseName this document resides in.
      * @param document     The document to index.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void putDocument(@NonNull String databaseName, @NonNull DocumentProto document)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         DocumentProto.Builder documentBuilder = document.toBuilder();
         rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ true);
 
@@ -270,12 +268,10 @@
      * @param uri          The URI of the document to get.
      * @return The Document contents, or {@code null} if no such URI exists in the system.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @Nullable
     public DocumentProto getDocument(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException, InterruptedException {
-        awaitInitialized();
+            @NonNull String uri) throws AppSearchException {
         GetResultProto getResultProto;
         mReadWriteLock.readLock().lock();
         try {
@@ -303,16 +299,13 @@
      * @return The results of performing this search  The proto might have no {@code results} if no
      * documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @NonNull
     public SearchResultProto query(
             @NonNull String databaseName,
             @NonNull SearchSpecProto searchSpec,
             @NonNull ResultSpecProto resultSpec,
-            @NonNull ScoringSpecProto scoringSpec) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            @NonNull ScoringSpecProto scoringSpec) throws AppSearchException {
         SearchSpecProto.Builder searchSpecBuilder = searchSpec.toBuilder();
         SearchResultProto searchResultProto;
         mReadWriteLock.readLock().lock();
@@ -347,13 +340,10 @@
      * @param nextPageToken The token of pre-loaded results of previously executed query.
      * @return The next page of results of previously executed query.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @NonNull
     public SearchResultProto getNextPage(@NonNull String databaseName, long nextPageToken)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken);
         checkSuccess(searchResultProto.getStatus());
         if (searchResultProto.getResultsCount() == 0) {
@@ -367,8 +357,7 @@
      * @param nextPageToken The token of pre-loaded results of previously executed query to be
      *                      Invalidated.
      */
-    public void invalidateNextPageToken(long nextPageToken) throws InterruptedException {
-        awaitInitialized();
+    public void invalidateNextPageToken(long nextPageToken) {
         mIcingSearchEngine.invalidateNextPageToken(nextPageToken);
     }
 
@@ -381,12 +370,9 @@
      * @param namespace    Namespace of the document to remove.
      * @param uri          URI of the document to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void remove(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            @NonNull String uri) throws AppSearchException {
         String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
@@ -407,12 +393,9 @@
      * @param databaseName The databaseName that contains documents of schemaType.
      * @param schemaType   The schemaType of documents to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeByType(@NonNull String databaseName, @NonNull String schemaType)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         String qualifiedType = getDatabasePrefix(databaseName) + schemaType;
         DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto;
         mReadWriteLock.writeLock().lock();
@@ -437,12 +420,9 @@
      * @param databaseName The databaseName that contains documents of namespace.
      * @param namespace    The namespace of documents to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeByNamespace(@NonNull String databaseName, @NonNull String namespace)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
         DeleteByNamespaceResultProto deleteByNamespaceResultProto;
         mReadWriteLock.writeLock().lock();
@@ -469,11 +449,9 @@
      *
      * @param databaseName The databaseName to remove all documents from.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeAll(@NonNull String databaseName)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
+            throws AppSearchException {
         mReadWriteLock.writeLock().lock();
         try {
             Set<String> existingNamespaces = mNamespaceMap.get(databaseName);
@@ -733,15 +711,6 @@
     }
 
     /**
-     * Waits for the instance to become initialized.
-     *
-     * @throws InterruptedException if the current thread was interrupted during waiting.
-     */
-    private void awaitInitialized() throws InterruptedException {
-        mInitCompleteLatch.await();
-    }
-
-    /**
      * Checks the given status code and throws an {@link AppSearchException} if code is an error.
      *
      * @throws AppSearchException on error codes.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java
new file mode 100644
index 0000000..fdeb90d
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2020 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.GenericDocument;
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+import com.google.protobuf.ByteString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Translates a {@link GenericDocument} into a {@link DocumentProto}.
+ * @hide
+ */
+
+public final class GenericDocumentToProtoConverter {
+    private GenericDocumentToProtoConverter() {}
+
+    /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */
+    @NonNull
+    @SuppressWarnings("unchecked")
+    public static DocumentProto convert(@NonNull GenericDocument document) {
+        Preconditions.checkNotNull(document);
+        Bundle properties = document.getBundle().getBundle(GenericDocument.PROPERTIES_FIELD);
+        DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
+        mProtoBuilder.setUri(document.getUri())
+                .setSchema(document.getSchemaType())
+                .setNamespace(document.getNamespace())
+                .setScore(document.getScore())
+                .setTtlMs(document.getTtlMillis())
+                .setCreationTimestampMs(document.getCreationTimestampMillis());
+        ArrayList<String> keys = new ArrayList<>(properties.keySet());
+        Collections.sort(keys);
+        for (int i = 0; i < keys.size(); i++) {
+            String name = keys.get(i);
+            Object values = properties.get(name);
+            PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name);
+            if (values instanceof boolean[]) {
+                for (boolean value : (boolean[]) values) {
+                    propertyProto.addBooleanValues(value);
+                }
+            } else if (values instanceof long[]) {
+                for (long value : (long[]) values) {
+                    propertyProto.addInt64Values(value);
+                }
+            } else if (values instanceof double[]) {
+                for (double value : (double[]) values) {
+                    propertyProto.addDoubleValues(value);
+                }
+            } else if (values instanceof String[]) {
+                for (String value : (String[]) values) {
+                    propertyProto.addStringValues(value);
+                }
+            } else if (values instanceof ArrayList) {
+                for (Bundle bundle : (ArrayList<Bundle>) values) {
+                    byte[] value = bundle.getByteArray(GenericDocument.BYTE_ARRAY_FIELD);
+                    propertyProto.addBytesValues(ByteString.copyFrom(value));
+                }
+            } else if (values instanceof Bundle[]) {
+                for (Bundle bundle : (Bundle[]) values) {
+                    GenericDocument value = new GenericDocument(bundle);
+                    propertyProto.addDocumentValues(convert(value));
+                }
+            } else {
+                throw new IllegalStateException(
+                        "Property \"" + name + "\" has unsupported value type \""
+                                + values.getClass().getSimpleName() + "\"");
+            }
+            mProtoBuilder.addProperties(propertyProto);
+        }
+        return mProtoBuilder.build();
+    }
+
+    /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */
+    @NonNull
+    public static GenericDocument convert(@NonNull DocumentProto proto) {
+        Preconditions.checkNotNull(proto);
+        GenericDocument.Builder<?> documentBuilder =
+                new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
+                        .setNamespace(proto.getNamespace())
+                        .setScore(proto.getScore())
+                        .setTtlMillis(proto.getTtlMs())
+                        .setCreationTimestampMillis(proto.getCreationTimestampMs());
+
+        for (int i = 0; i < proto.getPropertiesCount(); i++) {
+            PropertyProto property = proto.getProperties(i);
+            String name = property.getName();
+            if (property.getBooleanValuesCount() > 0) {
+                boolean[] values = new boolean[property.getBooleanValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getBooleanValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getInt64ValuesCount() > 0) {
+                long[] values = new long[property.getInt64ValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getInt64Values(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getDoubleValuesCount() > 0) {
+                double[] values = new double[property.getDoubleValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getDoubleValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getStringValuesCount() > 0) {
+                String[] values = new String[property.getStringValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getStringValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getBytesValuesCount() > 0) {
+                byte[][] values = new byte[property.getBytesValuesCount()][];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getBytesValues(j).toByteArray();
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getDocumentValuesCount() > 0) {
+                GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = convert(property.getDocumentValues(j));
+                }
+                documentBuilder.setProperty(name, values);
+            } else {
+                throw new IllegalStateException("Unknown type of value: " + name);
+            }
+        }
+        return documentBuilder.build();
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java
new file mode 100644
index 0000000..524c80d
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SnippetMatchProto;
+import com.google.android.icing.proto.SnippetProto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translates a {@link SearchResultProto} into {@link SearchResults}.
+ * @hide
+ */
+
+public class SearchResultToProtoConverter {
+    private SearchResultToProtoConverter() {}
+
+    /** Translates a {@link SearchResultProto} into a list of {@link SearchResult}. */
+    @NonNull
+    public static List<SearchResult> convert(@NonNull SearchResultProto searchResultProto) {
+        List<SearchResult> results = new ArrayList<>(searchResultProto.getResultsCount());
+        for (int i = 0; i < searchResultProto.getResultsCount(); i++) {
+            results.add(convertSearchResult(searchResultProto.getResults(i)));
+        }
+        return results;
+    }
+
+    /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */
+    @NonNull
+    static SearchResult convertSearchResult(@NonNull SearchResultProto.ResultProto proto) {
+        Bundle bundle = new Bundle();
+        GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument());
+        bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle());
+
+        ArrayList<Bundle> matchList = null;
+        if (proto.hasSnippet()) {
+            matchList = new ArrayList<>();
+            for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
+                SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
+                for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
+                    Bundle matchInfoBundle = convertToMatchInfoBundle(
+                            entry.getSnippetMatches(j), entry.getPropertyName());
+                    matchList.add(matchInfoBundle);
+                }
+            }
+        }
+        bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList);
+
+        return new SearchResult(bundle);
+    }
+
+    private static Bundle convertToMatchInfoBundle(
+            SnippetMatchProto snippetMatchProto, String propertyPath) {
+        Bundle bundle = new Bundle();
+        bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
+        bundle.putInt(
+                SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex());
+        bundle.putInt(
+                SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
+                snippetMatchProto.getExactMatchPosition());
+        bundle.putInt(
+                SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD,
+                snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes());
+        bundle.putInt(
+                SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD,
+                snippetMatchProto.getWindowPosition());
+        bundle.putInt(
+                SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD,
+                snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes());
+        return bundle;
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 129465a..b20b861 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1613,6 +1613,7 @@
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
+    field public static final int windowLayoutAffinity = 16844313; // 0x1010619
     field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
     field public static final int windowLightNavigationBar = 16844140; // 0x101056c
     field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -11714,10 +11715,7 @@
     method public android.graphics.drawable.Drawable getIcon(int);
     method public CharSequence getLabel();
     method public String getName();
-    method public float getProgress();
     method public android.os.UserHandle getUser();
-    method public boolean isLoading();
-    method public boolean isStartable();
   }
 
   public class LauncherApps {
@@ -11757,7 +11755,6 @@
     ctor public LauncherApps.Callback();
     method public abstract void onPackageAdded(String, android.os.UserHandle);
     method public abstract void onPackageChanged(String, android.os.UserHandle);
-    method public void onPackageProgressChanged(@NonNull String, @NonNull android.os.UserHandle, float);
     method public abstract void onPackageRemoved(String, android.os.UserHandle);
     method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean);
     method public void onPackagesSuspended(String[], android.os.UserHandle);
@@ -30255,9 +30252,12 @@
     method public int describeContents();
     method @NonNull public byte[] getKey();
     method @NonNull public String getName();
+    method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String AUTH_AES_XCBC = "xcbc(aes)";
     field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+    field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
     field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30265,6 +30265,7 @@
     field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
     field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
     field public static final String CRYPT_AES_CBC = "cbc(aes)";
+    field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
   }
 
   public final class IpSecManager {
@@ -31873,7 +31874,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
-    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -31953,6 +31954,8 @@
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index e825b62..993d31f 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -103,6 +103,10 @@
     field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0
   }
 
+  public final class PlaybackState implements android.os.Parcelable {
+    method public boolean isActiveState();
+  }
+
 }
 
 package android.net {
diff --git a/api/system-current.txt b/api/system-current.txt
index 25008f4..ff84613 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -225,6 +225,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -594,7 +595,7 @@
 
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
-    method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method public void setDontSendToRestrictedApps(boolean);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long);
     method public android.os.Bundle toBundle();
@@ -1802,6 +1803,7 @@
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
+    field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
@@ -5291,7 +5293,7 @@
     method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
     method public int getDstPort();
-    method public int getIpFilterContextId();
+    method @IntRange(from=0, to=61439) public int getIpFilterContextId();
     method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
     method public int getSrcPort();
     method public int getType();
@@ -5327,6 +5329,7 @@
     method public boolean isPrivateData();
     method public boolean isPtsPresent();
     method public boolean isSecureMemory();
+    method public void release();
   }
 
   public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5833,6 +5836,7 @@
     method public int getSymbolRate();
     method public int getType();
     method public int getVcmMode();
+    method public boolean isDiseqcRxMessage();
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
     field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5876,6 +5880,7 @@
   public static class DvbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
@@ -6077,14 +6082,21 @@
   public class FrontendStatus {
     method public int getAgc();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
+    method public int getBandwidth();
     method public int getBer();
+    method @NonNull public int[] getBers();
+    method @NonNull public int[] getCodeRates();
     method public int getFreqOffset();
+    method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
+    method @NonNull public int[] getInterleaving();
+    method @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
+    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -6093,23 +6105,34 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getSystemId();
+    method public int getTransmissionMode();
+    method @NonNull public int[] getTsDataRate();
+    method public int getUec();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLocked();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
     field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
+    field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
+    field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
     field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
     field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
+    field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+    field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
     field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
+    field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
@@ -6119,6 +6142,10 @@
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
+    field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
+    field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
+    field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20
+    field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c
   }
 
   public static class FrontendStatus.Atsc3PlpTuningInfo {
@@ -6282,7 +6309,9 @@
     method public void onHierarchyReported(int);
     method public void onInputStreamIdsReported(@NonNull int[]);
     method public void onLocked();
+    method public default void onModulationReported(int);
     method public void onPlpIdsReported(@NonNull int[]);
+    method public default void onPriorityReported(boolean);
     method public void onProgress(@IntRange(from=0, to=100) int);
     method public void onScanStopped();
     method public void onSignalTypeReported(int);
@@ -11443,6 +11472,10 @@
     field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
   }
 
+  public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+  }
+
   public final class SmsCbCmasInfo implements android.os.Parcelable {
     ctor public SmsCbCmasInfo(int, int, int, int, int, int);
     method public int describeContents();
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 1a44519..1d2090c 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -22,6 +22,8 @@
 #include <android-base/unique_fd.h>
 #include <binder/BinderService.h>
 
+#include <string>
+
 #include "android/os/BnIdmap2.h"
 
 namespace android::os {
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index ff45b14..bf31cbf 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -36,12 +36,11 @@
   void visit(const IdmapData::Header& header) override;
 
  private:
-  void Write(const void* value, size_t length);
   void Write8(uint8_t value);
   void Write16(uint16_t value);
   void Write32(uint32_t value);
   void WriteString256(const StringPiece& value);
-  void WriteString(const std::string& value);
+  void WriteString(const StringPiece& value);
   std::ostream& stream_;
 };
 
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 0f05592..a35fad9 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -17,48 +17,45 @@
 /*
  * # idmap file format (current version)
  *
- * idmap             := header data*
- * header            := magic version target_crc overlay_crc target_path overlay_path debug_info
- * data              := data_header data_block*
- * data_header       := target_package_id types_count
- * data_block        := target_type overlay_type entry_count entry_offset entry*
- * overlay_path      := string256
- * target_path       := string256
- * debug_info        := string
- * string            := <uint32_t> <uint8_t>+ '\0'+
- * entry             := <uint32_t>
- * entry_count       := <uint16_t>
- * entry_offset      := <uint16_t>
- * magic             := <uint32_t>
- * overlay_crc       := <uint32_t>
- * overlay_type      := <uint16_t>
- * string256         := <uint8_t>[256]
- * target_crc        := <uint32_t>
- * target_package_id := <uint16_t>
- * target_type       := <uint16_t>
- * types_count       := <uint16_t>
- * version           := <uint32_t>
+ * idmap                      := header data*
+ * header                     := magic version target_crc overlay_crc fulfilled_policies
+ *                               enforce_overlayable target_path overlay_path debug_info
+ * data                       := data_header target_entry* target_inline_entry* overlay_entry*
+ *                               string_pool
+ * data_header                := target_package_id overlay_package_id padding(2) target_entry_count
+ *                               target_inline_entry_count overlay_entry_count string_pool_index
+ * target_entry               := target_id overlay_id
+ * target_inline_entry        := target_id Res_value::size padding(1) Res_value::type
+ *                               Res_value::value
+ * overlay_entry              := overlay_id target_id
  *
- *
- * # idmap file format changelog
- * ## v1
- * - Identical to idmap v1.
- *
- * ## v2
- * - Entries are no longer separated by type into type specific data blocks.
- * - Added overlay-indexed target resource id lookup capabilities.
- * - Target and overlay entries are stored as a sparse array in the data block. The target entries
- *   array maps from target resource id to overlay data type and value and the array is sorted by
- *   target resource id. The overlay entries array maps from overlay resource id to target resource
- *   id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
- *   to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
- *   mappings at runtime.
- * - Idmap can now encode a type and value to override a resource without needing a table entry.
- * - A string pool block is included to retrieve the value of strings that do not have a resource
- *   table entry.
- *
- * ## v3
- * - Add 'debug' block to IdmapHeader.
+ * debug_info                 := string
+ * enforce_overlayable        := <uint32_t>
+ * fulfilled_policies         := <uint32_t>
+ * magic                      := <uint32_t>
+ * overlay_crc                := <uint32_t>
+ * overlay_entry_count        := <uint32_t>
+ * overlay_id                 := <uint32_t>
+ * overlay_package_id         := <uint8_t>
+ * overlay_path               := string256
+ * padding(n)                 := <uint8_t>[n]
+ * Res_value::size            := <uint16_t>
+ * Res_value::type            := <uint8_t>
+ * Res_value::value           := <uint32_t>
+ * string                     := <uint32_t> <uint8_t>+ padding(n)
+ * string256                  := <uint8_t>[256]
+ * string_pool                := string
+ * string_pool_index          := <uint32_t>
+ * string_pool_length         := <uint32_t>
+ * target_crc                 := <uint32_t>
+ * target_entry_count         := <uint32_t>
+ * target_inline_entry_count  := <uint32_t>
+ * target_id                  := <uint32_t>
+ * target_package_id          := <uint8_t>
+ * target_path                := string256
+ * value_type                 := <uint8_t>
+ * value_data                 := <uint32_t>
+ * version                    := <uint32_t>
  */
 
 #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -183,6 +180,10 @@
       return target_entry_count;
     }
 
+    inline uint32_t GetTargetInlineEntryCount() const {
+      return target_entry_inline_count;
+    }
+
     inline uint32_t GetOverlayEntryCount() const {
       return overlay_entry_count;
     }
@@ -191,19 +192,15 @@
       return string_pool_index_offset;
     }
 
-    inline uint32_t GetStringPoolLength() const {
-      return string_pool_len;
-    }
-
     void accept(Visitor* v) const;
 
    private:
     PackageId target_package_id_;
     PackageId overlay_package_id_;
     uint32_t target_entry_count;
+    uint32_t target_entry_inline_count;
     uint32_t overlay_entry_count;
     uint32_t string_pool_index_offset;
-    uint32_t string_pool_len;
     Header() = default;
 
     friend Idmap;
@@ -213,8 +210,12 @@
 
   struct TargetEntry {
     ResourceId target_id;
-    TargetValue::DataType data_type;
-    TargetValue::DataValue data_value;
+    ResourceId overlay_id;
+  };
+
+  struct TargetInlineEntry {
+    ResourceId target_id;
+    TargetValue value;
   };
 
   struct OverlayEntry {
@@ -227,20 +228,24 @@
   static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
       const ResourceMapping& resource_mapping);
 
-  inline const std::unique_ptr<const Header>& GetHeader() const {
+  const std::unique_ptr<const Header>& GetHeader() const {
     return header_;
   }
 
-  inline const std::vector<TargetEntry>& GetTargetEntries() const {
+  const std::vector<TargetEntry>& GetTargetEntries() const {
     return target_entries_;
   }
 
-  inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
+  const std::vector<TargetInlineEntry>& GetTargetInlineEntries() const {
+    return target_inline_entries_;
+  }
+
+  const std::vector<OverlayEntry>& GetOverlayEntries() const {
     return overlay_entries_;
   }
 
-  inline const void* GetStringPoolData() const {
-    return string_pool_.get();
+  const std::string& GetStringPoolData() const {
+    return string_pool_data_;
   }
 
   void accept(Visitor* v) const;
@@ -251,8 +256,9 @@
 
   std::unique_ptr<const Header> header_;
   std::vector<TargetEntry> target_entries_;
+  std::vector<TargetInlineEntry> target_inline_entries_;
   std::vector<OverlayEntry> overlay_entries_;
-  std::unique_ptr<uint8_t[]> string_pool_;
+  std::string string_pool_data_;
 
   friend Idmap;
   DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -304,6 +310,10 @@
   virtual void visit(const IdmapData::Header& header) = 0;
 };
 
+inline size_t CalculatePadding(size_t data_length) {
+  return (4 - (data_length % 4)) % 4;
+}
+
 }  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 5dcf217..2b4c761 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -41,8 +41,9 @@
 
  private:
   std::ostream& stream_;
-  std::unique_ptr<const ApkAssets> target_apk_;
   AssetManager2 target_am_;
+  AssetManager2 overlay_am_;
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
 };
 
 }  // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 92c1864..58edc99 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -45,11 +45,9 @@
   void print(uint16_t value, const char* fmt, ...);
   void print(uint32_t value, const char* fmt, ...);
   void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
-  void print_raw(uint32_t length, const char* fmt, ...);
 
   std::ostream& stream_;
-  std::unique_ptr<const ApkAssets> target_apk_;
-  std::unique_ptr<const ApkAssets> overlay_apk_;
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
   AssetManager2 target_am_;
   AssetManager2 overlay_am_;
   size_t offset_;
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 5869409..0a58ec4 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -41,7 +41,7 @@
   DataValue data_value;
 };
 
-using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
 using OverlayResourceMap = std::map<ResourceId, ResourceId>;
 
 class ResourceMapping {
@@ -56,7 +56,7 @@
                                                bool enforce_overlayable, LogInfo& log_info);
 
   // Retrieves the mapping of target resource id to overlay value.
-  inline TargetResourceMap GetTargetToOverlayMap() const {
+  inline const TargetResourceMap& GetTargetToOverlayMap() const {
     return target_map_;
   }
 
@@ -81,19 +81,24 @@
   }
 
   // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
-  inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
-    return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+  inline const StringPiece GetStringPoolData() const {
+    return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
+                       string_pool_data_length_);
   }
 
  private:
   ResourceMapping() = default;
 
-  // Apps a mapping of target resource id to the type and value of the data that overlays the
-  // target resource. The data_type is the runtime format of the data value (see
-  // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+  // Maps a target resource id to an overlay resource id.
+  // If rewrite_overlay_reference is `true` then references to the overlay
   // resource should appear as a reference to its corresponding target resource at runtime.
+  Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
+                          bool rewrite_overlay_reference);
+
+  // Maps a target resource id to a data type and value combination.
+  // The `data_type` is the runtime format of the data value (see Res_value::dataType).
   Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
-                          TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+                          TargetValue::DataValue data_value);
 
   // Removes the overlay value mapping for the target resource.
   void RemoveMapping(ResourceId target_resource);
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 255212a..726f6c5 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -24,10 +24,6 @@
 
 namespace android::idmap2 {
 
-void BinaryStreamVisitor::Write(const void* value, size_t length) {
-  stream_.write(reinterpret_cast<const char*>(value), length);
-}
-
 void BinaryStreamVisitor::Write8(uint8_t value) {
   stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
 }
@@ -49,11 +45,11 @@
   stream_.write(buf, sizeof(buf));
 }
 
-void BinaryStreamVisitor::WriteString(const std::string& value) {
-  // pad with null to nearest word boundary; include at least one terminating null
-  size_t padding_size = 4 - (value.size() % 4);
-  Write32(value.size() + padding_size);
-  stream_.write(value.c_str(), value.size());
+void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+  // pad with null to nearest word boundary;
+  size_t padding_size = CalculatePadding(value.size());
+  Write32(value.size());
+  stream_.write(value.data(), value.size());
   stream_.write("\0\0\0\0", padding_size);
 }
 
@@ -67,7 +63,7 @@
   Write32(header.GetTargetCrc());
   Write32(header.GetOverlayCrc());
   Write32(header.GetFulfilledPolicies());
-  Write8(static_cast<uint8_t>(header.GetEnforceOverlayable()));
+  Write32(static_cast<uint8_t>(header.GetEnforceOverlayable()));
   WriteString256(header.GetTargetPath());
   WriteString256(header.GetOverlayPath());
   WriteString(header.GetDebugInfo());
@@ -76,8 +72,16 @@
 void BinaryStreamVisitor::visit(const IdmapData& data) {
   for (const auto& target_entry : data.GetTargetEntries()) {
     Write32(target_entry.target_id);
-    Write8(target_entry.data_type);
-    Write32(target_entry.data_value);
+    Write32(target_entry.overlay_id);
+  }
+
+  static constexpr uint16_t kValueSize = 8U;
+  for (const auto& target_entry : data.GetTargetInlineEntries()) {
+    Write32(target_entry.target_id);
+    Write16(kValueSize);
+    Write8(0U);  // padding
+    Write8(target_entry.value.data_type);
+    Write32(target_entry.value.data_value);
   }
 
   for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -85,16 +89,18 @@
     Write32(overlay_entry.target_id);
   }
 
-  Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength());
+  WriteString(data.GetStringPoolData());
 }
 
 void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
   Write8(header.GetTargetPackageId());
   Write8(header.GetOverlayPackageId());
+  Write8(0U);  // padding
+  Write8(0U);  // padding
   Write32(header.GetTargetEntryCount());
+  Write32(header.GetTargetInlineEntryCount());
   Write32(header.GetOverlayEntryCount());
   Write32(header.GetStringPoolIndexOffset());
-  Write32(header.GetStringPoolLength());
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 23c25a7..1129413 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -51,19 +51,19 @@
   return false;
 }
 
-bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
-  uint32_t value;
-  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
-    *out = dtohl(value);
+bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+  uint16_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
+    *out = dtohs(value);
     return true;
   }
   return false;
 }
 
-bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) {
-  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
-  if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) {
-    *out = std::move(buffer);
+bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+  uint32_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
+    *out = dtohl(value);
     return true;
   }
   return false;
@@ -95,8 +95,11 @@
   if (!stream.read(buf.data(), size)) {
     return Error("failed to read string of size %u", size);
   }
-  // buf is guaranteed to be null terminated (with enough nulls to end on a word boundary)
-  buf.resize(strlen(buf.c_str()));
+  uint32_t padding_size = CalculatePadding(size);
+  std::string padding(padding_size, '\0');
+  if (!stream.read(padding.data(), padding_size)) {
+    return Error("failed to read string padding of size %u", padding_size);
+  }
   return buf;
 }
 
@@ -112,16 +115,16 @@
 
 std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
   std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
-  uint8_t enforce_overlayable;
+  uint32_t enforce_overlayable;
   if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
       !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
-      !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) ||
-      !ReadString256(stream, idmap_header->target_path_) ||
+      !Read32(stream, &idmap_header->fulfilled_policies_) ||
+      !Read32(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) ||
       !ReadString256(stream, idmap_header->overlay_path_)) {
     return nullptr;
   }
 
-  idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable);
+  idmap_header->enforce_overlayable_ = enforce_overlayable != 0U;
 
   auto debug_str = ReadString(stream);
   if (!debug_str) {
@@ -207,12 +210,13 @@
 std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
   std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
 
+  uint8_t padding;
   if (!Read8(stream, &idmap_data_header->target_package_id_) ||
-      !Read8(stream, &idmap_data_header->overlay_package_id_) ||
-      !Read32(stream, &idmap_data_header->target_entry_count) ||
+      !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
+      !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
+      !Read32(stream, &idmap_data_header->target_entry_inline_count) ||
       !Read32(stream, &idmap_data_header->overlay_entry_count) ||
-      !Read32(stream, &idmap_data_header->string_pool_index_offset) ||
-      !Read32(stream, &idmap_data_header->string_pool_len)) {
+      !Read32(stream, &idmap_data_header->string_pool_index_offset)) {
     return nullptr;
   }
 
@@ -225,14 +229,27 @@
   if (!data->header_) {
     return nullptr;
   }
+
   // Read the mapping of target resource id to overlay resource value.
   for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
     TargetEntry target_entry{};
-    if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) ||
-        !Read32(stream, &target_entry.data_value)) {
+    if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
       return nullptr;
     }
-    data->target_entries_.emplace_back(target_entry);
+    data->target_entries_.push_back(target_entry);
+  }
+
+  // Read the mapping of target resource id to inline overlay values.
+  uint8_t unused1;
+  uint16_t unused2;
+  for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
+    TargetInlineEntry target_entry{};
+    if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
+        !Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
+        !Read32(stream, &target_entry.value.data_value)) {
+      return nullptr;
+    }
+    data->target_inline_entries_.push_back(target_entry);
   }
 
   // Read the mapping of overlay resource id to target resource id.
@@ -245,9 +262,11 @@
   }
 
   // Read raw string pool bytes.
-  if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) {
+  auto string_pool_data = ReadString(stream);
+  if (!string_pool_data) {
     return nullptr;
   }
+  data->string_pool_data_ = std::move(*string_pool_data);
 
   return std::move(data);
 }
@@ -290,27 +309,28 @@
   }
 
   std::unique_ptr<IdmapData> data(new IdmapData());
-  for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) {
-    data->target_entries_.emplace_back(IdmapData::TargetEntry{
-        mappings.first, mappings.second.data_type, mappings.second.data_value});
+  data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
+  for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
+    if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
+      data->target_entries_.push_back({mapping.first, *overlay_resource});
+    } else {
+      data->target_inline_entries_.push_back(
+          {mapping.first, std::get<TargetValue>(mapping.second)});
+    }
   }
 
-  for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) {
-    data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second});
+  for (const auto& mapping : resource_mapping.GetOverlayToTargetMap()) {
+    data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mapping.first, mapping.second});
   }
 
   std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
   data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
   data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
   data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
+  data_header->target_entry_inline_count =
+      static_cast<uint32_t>(data->target_inline_entries_.size());
   data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
   data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
-
-  const auto string_pool_data = resource_mapping.GetStringPoolData();
-  data_header->string_pool_len = string_pool_data.second;
-  data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
-  memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
-
   data->header_ = std::move(data_header);
   return {std::move(data)};
 }
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 63ee8a6..a93202a 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -38,6 +38,7 @@
   stream_ << "Paths:" << std::endl
           << TAB "target apk path  : " << header.GetTargetPath() << std::endl
           << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+
   const std::string& debug = header.GetDebugInfo();
   if (!debug.empty()) {
     std::istringstream debug_stream(debug);
@@ -48,10 +49,16 @@
     }
   }
 
-  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
-  if (target_apk_) {
+  if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string())) {
     target_am_.SetApkAssets({target_apk_.get()});
+    apk_assets_.push_back(std::move(target_apk_));
   }
+
+  if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath().to_string())) {
+    overlay_am_.SetApkAssets({overlay_apk.get()});
+    apk_assets_.push_back(std::move(overlay_apk));
+  }
+
   stream_ << "Mapping:" << std::endl;
 }
 
@@ -59,34 +66,56 @@
 }
 
 void PrettyPrintVisitor::visit(const IdmapData& data) {
+  static constexpr const char* kUnknownResourceName = "???";
+
   const bool target_package_loaded = !target_am_.GetApkAssets().empty();
-  const ResStringPool string_pool(data.GetStringPoolData(),
-                                  data.GetHeader()->GetStringPoolLength());
+  const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
+
+  const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
   const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
 
-  for (auto& target_entry : data.GetTargetEntries()) {
-    stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id);
-
-    if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
-        target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
-      stream_ << " " << utils::DataTypeToString(target_entry.data_type);
-    }
-
-    if (target_entry.data_type == Res_value::TYPE_STRING) {
-      stream_ << " \""
-              << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
-              << "\"";
-    } else {
-      stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
-    }
-
+  for (const auto& target_entry : data.GetTargetEntries()) {
+    std::string target_name = kUnknownResourceName;
     if (target_package_loaded) {
-      Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
-      if (name) {
-        stream_ << " " << *name;
+      if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+        target_name = *name;
       }
     }
-    stream_ << std::endl;
+
+    std::string overlay_name = kUnknownResourceName;
+    if (overlay_package_loaded) {
+      if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
+        overlay_name = *name;
+      }
+    }
+
+    stream_ << TAB
+            << base::StringPrintf("0x%08x -> 0x%08x (%s -> %s)", target_entry.target_id,
+                                  target_entry.overlay_id, target_name.c_str(),
+                                  overlay_name.c_str())
+            << std::endl;
+  }
+
+  for (auto& target_entry : data.GetTargetInlineEntries()) {
+    stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
+            << utils::DataTypeToString(target_entry.value.data_type);
+
+    size_t unused;
+    if (target_entry.value.data_type == Res_value::TYPE_STRING) {
+      auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused);
+      stream_ << " \"" << StringPiece16(str) << "\"";
+    } else {
+      stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
+    }
+
+    std::string target_name = kUnknownResourceName;
+    if (target_package_loaded) {
+      if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+        target_name = *name;
+      }
+    }
+
+    stream_ << " (" << target_name << ")" << std::endl;
   }
 }
 
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 3f62a2a..82f5d26 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -30,15 +30,6 @@
 using android::ApkAssets;
 using android::idmap2::policy::PoliciesToDebugString;
 
-namespace {
-
-size_t StringSizeWhenEncoded(const std::string& s) {
-  size_t null_bytes = 4 - (s.size() % 4);
-  return sizeof(uint32_t) + s.size() + null_bytes;
-}
-
-}  // namespace
-
 namespace android::idmap2 {
 
 void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
@@ -51,19 +42,24 @@
   print(header.GetOverlayCrc(), "overlay crc");
   print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
         PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
-  print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable");
+  print(static_cast<uint32_t>(header.GetEnforceOverlayable()), "enforce overlayable");
   print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
   print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
-  print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info");
 
-  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+  uint32_t debug_info_size = header.GetDebugInfo().size();
+  print(debug_info_size, "debug info size");
+  print("...", debug_info_size + CalculatePadding(debug_info_size), "debug info");
+
+  auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
   if (target_apk_) {
     target_am_.SetApkAssets({target_apk_.get()});
+    apk_assets_.push_back(std::move(target_apk_));
   }
 
-  overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+  auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
   if (overlay_apk_) {
     overlay_am_.SetApkAssets({overlay_apk_.get()});
+    apk_assets_.push_back(std::move(overlay_apk_));
   }
 }
 
@@ -82,18 +78,44 @@
       print(target_entry.target_id, "target id");
     }
 
-    print(target_entry.data_type, "type: %s",
-          utils::DataTypeToString(target_entry.data_type).data());
-
     Result<std::string> overlay_name(Error(""));
-    if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE ||
-                                   target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
-      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
+    if (overlay_package_loaded) {
+      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
     }
     if (overlay_name) {
-      print(target_entry.data_value, "value: %s", overlay_name->c_str());
+      print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
     } else {
-      print(target_entry.data_value, "value");
+      print(target_entry.overlay_id, "overlay id");
+    }
+  }
+
+  for (auto& target_entry : data.GetTargetInlineEntries()) {
+    Result<std::string> target_name(Error(""));
+    if (target_package_loaded) {
+      target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+    }
+    if (target_name) {
+      print(target_entry.target_id, "target id: %s", target_name->c_str());
+    } else {
+      print(target_entry.target_id, "target id");
+    }
+
+    print("...", sizeof(Res_value::size) + sizeof(Res_value::res0), "padding");
+
+    print(target_entry.value.data_type, "type: %s",
+          utils::DataTypeToString(target_entry.value.data_type).data());
+
+    Result<std::string> overlay_name(Error(""));
+    if (overlay_package_loaded &&
+        (target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
+         target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
+    }
+
+    if (overlay_name) {
+      print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
+    } else {
+      print(target_entry.value.data_value, "data");
     }
   }
 
@@ -121,19 +143,19 @@
     }
   }
 
-  const size_t string_pool_length = data.GetHeader()->GetStringPoolLength();
-  if (string_pool_length > 0) {
-    print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length);
-  }
+  uint32_t string_pool_size = data.GetStringPoolData().size();
+  print(string_pool_size, "string pool size");
+  print("...", string_pool_size + CalculatePadding(string_pool_size), "string pool");
 }
 
 void RawPrintVisitor::visit(const IdmapData::Header& header) {
   print(header.GetTargetPackageId(), "target package id");
   print(header.GetOverlayPackageId(), "overlay package id");
+  print("...", sizeof(Idmap_data_header::p0), "padding");
   print(header.GetTargetEntryCount(), "target entry count");
+  print(header.GetTargetInlineEntryCount(), "target inline entry count");
   print(header.GetOverlayEntryCount(), "overlay entry count");
   print(header.GetStringPoolIndexOffset(), "string pool index offset");
-  print(header.GetStringPoolLength(), "string pool byte length");
 }
 
 // NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -190,17 +212,4 @@
   offset_ += encoded_size;
 }
 
-// NOLINTNEXTLINE(cert-dcl50-cpp)
-void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  std::string comment;
-  base::StringAppendV(&comment, fmt, ap);
-  va_end(ap);
-
-  stream_ << base::StringPrintf("%08zx: ", offset_) << "........  " << comment << std::endl;
-
-  offset_ += length;
-}
-
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index fd8b4eb..31f1c16 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -71,9 +71,9 @@
   if (!target_package.DefinesOverlayable()) {
     return (sDefaultPolicies & fulfilled_policies) != 0
                ? Result<Unit>({})
-               : Error(
-                     "overlay must be preinstalled or signed with the same signature as the "
-                     "target");
+               : Error("overlay must be preinstalled, signed with the same signature as the target,"
+                       " or signed with the same signature as the package referenced through"
+                       " <overlay-config-signature>.");
   }
 
   const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
@@ -205,19 +205,14 @@
       overlay_resource->data += string_pool_offset;
     }
 
-    // Only rewrite resources defined within the overlay package to their corresponding target
-    // resource ids at runtime.
-    bool rewrite_overlay_reference =
-        IsReference(overlay_resource->dataType)
-            ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
-            : false;
-
-    if (rewrite_overlay_reference) {
-      overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+    if (IsReference(overlay_resource->dataType)) {
+      // Only rewrite resources defined within the overlay package to their corresponding target
+      // resource ids at runtime.
+      bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
+      resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
+    } else {
+      resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
     }
-
-    resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
-                                rewrite_overlay_reference);
   }
 
   return resource_mapping;
@@ -246,9 +241,8 @@
 
     // Retrieve the compile-time resource id of the target resource.
     target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
-
-    resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
-                                /* rewrite_overlay_reference */ false);
+    resource_mapping.AddMapping(target_resource, overlay_resid,
+                                false /* rewrite_overlay_reference */);
   }
 
   return resource_mapping;
@@ -396,9 +390,7 @@
   return map;
 }
 
-Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
-                                         TargetValue::DataType data_type,
-                                         TargetValue::DataValue data_value,
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
                                          bool rewrite_overlay_reference) {
   if (target_map_.find(target_resource) != target_map_.end()) {
     return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
@@ -407,13 +399,26 @@
   // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
   // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
 
-  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+  target_map_.insert(std::make_pair(target_resource, overlay_resource));
 
-  if (rewrite_overlay_reference && IsReference(data_type)) {
-    overlay_map_.insert(std::make_pair(data_value, target_resource));
+  if (rewrite_overlay_reference) {
+    overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
+  }
+  return Unit{};
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+                                         TargetValue::DataType data_type,
+                                         TargetValue::DataValue data_value) {
+  if (target_map_.find(target_resource) != target_map_.end()) {
+    return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
   }
 
-  return Result<Unit>({});
+  // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+  // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+  return Unit{};
 }
 
 void ResourceMapping::RemoveMapping(ResourceId target_resource) {
@@ -422,14 +427,15 @@
     return;
   }
 
-  const TargetValue value = target_iter->second;
+  const auto value = target_iter->second;
   target_map_.erase(target_iter);
 
-  if (!IsReference(value.data_type)) {
+  const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
+  if (overlay_resource == nullptr) {
     return;
   }
 
-  auto overlay_iter = overlay_map_.equal_range(value.data_value);
+  auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
   for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
     if (i->second == target_resource) {
       overlay_map_.erase(i);
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 5fea7bc..c3a3e0b 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -72,13 +72,20 @@
   const auto& target_entries2 = data2->GetTargetEntries();
   ASSERT_EQ(target_entries1.size(), target_entries2.size());
   ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
-  ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value);
+  ASSERT_EQ(target_entries1[0].overlay_id, target_entries2[0].overlay_id);
 
   ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
-  ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value);
+  ASSERT_EQ(target_entries1[1].overlay_id, target_entries2[1].overlay_id);
 
   ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
-  ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value);
+  ASSERT_EQ(target_entries1[2].overlay_id, target_entries2[2].overlay_id);
+
+  const auto& target_inline_entries1 = data1->GetTargetInlineEntries();
+  const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
+  ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
+  ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
+  ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
 
   const auto& overlay_entries1 = data1->GetOverlayEntries();
   const auto& overlay_entries2 = data2->GetOverlayEntries();
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 61751b3..e7e9e4c 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -128,13 +128,13 @@
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+  ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002"),
             std::string::npos);
 
   // clang-format off
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 6fab5e0..9b42a27 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -42,14 +42,18 @@
 
 namespace android::idmap2 {
 
-#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
-  ASSERT_EQ(entry.target_id, target_resid);                   \
-  ASSERT_EQ(entry.data_type, type);                           \
-  ASSERT_EQ(entry.data_value, value)
+#define ASSERT_TARGET_ENTRY(entry, target_resid, overlay_resid) \
+  ASSERT_EQ((entry).target_id, (target_resid));                 \
+  ASSERT_EQ((entry).overlay_id, (overlay_resid))
+
+#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
+  ASSERT_EQ((entry).target_id, target_resid);                                          \
+  ASSERT_EQ((entry).value.data_type, (expected_type));                                 \
+  ASSERT_EQ((entry).value.data_value, (expected_value))
 
 #define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
-  ASSERT_EQ(entry.overlay_id, overlay_resid);                    \
-  ASSERT_EQ(entry.target_id, target_resid)
+  ASSERT_EQ((entry).overlay_id, (overlay_resid));                \
+  ASSERT_EQ((entry).target_id, (target_resid))
 
 TEST(IdmapTests, TestCanonicalIdmapPathFor) {
   ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
@@ -62,7 +66,7 @@
   std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
   ASSERT_EQ(header->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(header->GetVersion(), 0x04U);
+  ASSERT_EQ(header->GetVersion(), 0x05U);
   ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -75,7 +79,7 @@
 TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
   // overwrite the target path string, including the terminating null, with '.'
-  for (size_t i = 0x15; i < 0x115; i++) {
+  for (size_t i = 0x18; i < 0x118; i++) {
     raw[i] = '.';
   }
   std::istringstream stream(raw);
@@ -84,7 +88,7 @@
 }
 
 TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
-  const size_t offset = 0x221;
+  const size_t offset = 0x224;
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
                   idmap_raw_data_len - offset);
   std::istringstream stream(raw);
@@ -96,7 +100,7 @@
 }
 
 TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
-  const size_t offset = 0x221;
+  const size_t offset = 0x224;
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
                   idmap_raw_data_len - offset);
   std::istringstream stream(raw);
@@ -106,12 +110,14 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 3U);
-  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f020000);
-  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f030000);
-  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f030001);
+  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
+  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
+  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 1U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
+                             0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -130,7 +136,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
@@ -146,9 +152,14 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 3U);
-  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000);
-  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000);
-  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001);
+  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
+  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
+  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 1U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
+                             0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -184,7 +195,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -244,14 +255,13 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 4U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
-                      Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str1);
-  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str3);
-  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str4);
+  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, R::overlay::integer::int1);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay::string::str1);
+  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay::string::str4);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 4U);
@@ -286,13 +296,13 @@
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 4U);
   ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
-                      Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str1);
-  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str3);
-  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str4);
+                      R::overlay_shared::integer::int1);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay_shared::string::str1);
+  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay_shared::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay_shared::string::str4);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 4U);
@@ -320,10 +330,12 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 2U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+  ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1,
                       0x0104000a);  // -> android:string/ok
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, R::overlay::string::str3);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(overlay_entries.size(), 1U);
@@ -342,13 +354,17 @@
   ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
   auto& data = *idmap_data;
 
-  constexpr size_t overlay_string_pool_size = 8U;
   const auto& target_entries = data->GetTargetEntries();
-  ASSERT_EQ(target_entries.size(), 2U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
-                      73U);  // -> 73
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
-                      overlay_string_pool_size + 0U);  // -> "Hello World"
+  ASSERT_EQ(target_entries.size(), 0U);
+
+  constexpr size_t overlay_string_pool_size = 8U;
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 2U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+                             Res_value::TYPE_INT_DEC, 73U);  // -> 73
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+                             Res_value::TYPE_STRING,
+                             overlay_string_pool_size + 0U);  // -> "Hello World"
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(overlay_entries.size(), 0U);
@@ -479,9 +495,9 @@
   ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
                                               PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
 
-  // target path: bytes (0x15, 0x114)
+  // target path: bytes (0x18, 0x117)
   std::string bad_target_path_string(stream.str());
-  bad_target_path_string[0x15] = '\0';
+  bad_target_path_string[0x18] = '\0';
   std::stringstream bad_target_path_stream(bad_target_path_string);
   std::unique_ptr<const IdmapHeader> bad_target_path_header =
       IdmapHeader::FromBinaryStream(bad_target_path_stream);
@@ -490,9 +506,9 @@
   ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
                                             PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
 
-  // overlay path: bytes (0x115, 0x214)
+  // overlay path: bytes (0x118, 0x217)
   std::string bad_overlay_path_string(stream.str());
-  bad_overlay_path_string[0x115] = '\0';
+  bad_overlay_path_string[0x118] = '\0';
   std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
   std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
       IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 9a10079..d30fbfc 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -56,7 +56,8 @@
 
   ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
   ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
-  ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+  ASSERT_NE(stream.str().find(R::target::integer::literal::int1 +
+                              " -> 0x7f010000 (integer/int1 -> integer/int1)\n"),
             std::string::npos);
 }
 
@@ -75,7 +76,7 @@
 
   ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
   ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
-  ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b268d5a..95bd9473 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -65,7 +65,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000005  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(
       StringPrintf(ADDRESS "%s  target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
       stream.str());
@@ -73,19 +73,19 @@
       StringPrintf(ADDRESS "%s  overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
       stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000001  fulfilled policies: public\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  enforce overlayable\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  enforce overlayable\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  target package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  overlay package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  target entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000008  string pool index offset\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "000000b4  string pool byte length\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  target id: integer/int1\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      07  type: reference \\(dynamic\\)\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  value: integer/int1\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  overlay id: integer/int1\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  overlay id: integer/int1\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  target id: integer/int1\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "000000b4  string pool size\n", stream.str());
+  ASSERT_CONTAINS_REGEX("000002bc: ........  string pool: ...\n", stream.str());
 }
 
 TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -102,22 +102,26 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000005  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00001234  target crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00005678  overlay crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000011  fulfilled policies: public|signature\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  enforce overlayable\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  enforce overlayable\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  target package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  overlay package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  target entry count\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  target inline entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000000  string pool index offset\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000000  string pool byte length\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  type: reference\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  value\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "      11  type: integer\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "12345678  data\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030002  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  string pool size\n", stream.str());
+  ASSERT_CONTAINS_REGEX("00000278: ........  string pool: ...\n", stream.str());
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 3ec6ac2..185e929 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -77,30 +77,61 @@
                                 fulfilled_policies, enforce_overlayable);
 }
 
-Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
-                           const uint8_t type, const uint32_t value, bool rewrite) {
+Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
+                           ResourceId overlay_resource, bool rewrite) {
   auto target_map = mapping.GetTargetToOverlayMap();
   auto entry_map = target_map.find(target_resource);
   if (entry_map == target_map.end()) {
     return Error("Failed to find mapping for target resource");
   }
 
-  if (entry_map->second.data_type != type) {
-    return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
-                 entry_map->second.data_type);
+  auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
+  if (actual_overlay_resource == nullptr) {
+    return Error("Target resource is not mapped to an overlay resource id");
   }
 
-  if (entry_map->second.data_value != value) {
-    return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
-                 entry_map->second.data_value);
+  if (*actual_overlay_resource != overlay_resource) {
+    return Error(R"(Expected id: "0x%02x" Actual id: "0x%02x")", overlay_resource,
+                 *actual_overlay_resource);
   }
 
   auto overlay_map = mapping.GetOverlayToTargetMap();
-  auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+  auto overlay_iter = overlay_map.find(overlay_resource);
   if ((overlay_iter != overlay_map.end()) != rewrite) {
     return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
   }
 
+  if (rewrite && overlay_iter->second != target_resource) {
+    return Error(R"(Expected rewrite id: "0x%02x" Actual id: "0x%02x")", target_resource,
+                 overlay_iter->second);
+  }
+
+  return Result<Unit>({});
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+                           const uint8_t type, const uint32_t value) {
+  auto target_map = mapping.GetTargetToOverlayMap();
+  auto entry_map = target_map.find(target_resource);
+  if (entry_map == target_map.end()) {
+    return Error("Failed to find mapping for target resource");
+  }
+
+  auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
+  if (actual_overlay_value == nullptr) {
+    return Error("Target resource is not mapped to an inline value");
+  }
+
+  if (actual_overlay_value->data_type != type) {
+    return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+                 actual_overlay_value->data_type);
+  }
+
+  if (actual_overlay_value->data_value != value) {
+    return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+                 actual_overlay_value->data_value);
+  }
+
   return Result<Unit>({});
 }
 
@@ -116,14 +147,14 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
-                              R::overlay::integer::int1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str3, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str4, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
 }
 
 TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
@@ -138,12 +169,12 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str4, true /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str1, true /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str3, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str4, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str1, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str3, true /* rewrite */));
 }
 
 TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
@@ -159,10 +190,9 @@
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
   ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+  ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a,
                               false /* rewrite */));  // -> android:string/ok
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              0x7f020001, true /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::str3, 0x7f020001, true /* rewrite */));
 }
 
 TEST(ResourceMappingTests, InlineResources) {
@@ -180,10 +210,8 @@
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
   ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
   ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
-                              overlay_string_pool_size + 0U,
-                              false /* rewrite */));  // -> "Hello World"
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
-                              false /* rewrite */));  // -> 73
+                              overlay_string_pool_size + 0U));  // -> "Hello World"
+  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
 }
 
 TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
@@ -195,13 +223,13 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay::string::policy_public, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay::string::policy_system, false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -215,15 +243,15 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay_invalid::string::policy_public,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay_invalid::string::policy_system,
                               false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay_invalid::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -238,37 +266,36 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
                               R::system_overlay_invalid::string::not_overlayable,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::other,
                               R::system_overlay_invalid::string::other, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
                               R::system_overlay_invalid::string::policy_actor,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
                               R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
                               R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
                               R::system_overlay_invalid::string::policy_product,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay_invalid::string::policy_public,
                               false /* rewrite */));
   ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
-                              Res_value::TYPE_REFERENCE,
                               R::system_overlay_invalid::string::policy_config_signature,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
                               R::system_overlay_invalid::string::policy_signature,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay_invalid::string::policy_system,
                               false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay_invalid::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Overlays that do not target an <overlayable> tag can overlay resources defined within any
@@ -281,14 +308,14 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
-                              R::overlay::integer::int1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str3, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str4, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
 }
 
 // Overlays that are neither pre-installed nor signed with the same signature as the target cannot
@@ -302,9 +329,9 @@
   ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
 }
 
-// Overlays that are pre-installed or are signed with the same signature as the target  or are signed
-// with the same signature as the reference package can overlay packages that have not defined
-// overlayable resources.
+// Overlays that are pre-installed or are signed with the same signature as the target  or are
+// signed with the same signature as the reference package can overlay packages that have not
+// defined overlayable resources.
 TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
   auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
     auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
@@ -315,39 +342,38 @@
     ASSERT_TRUE(resources) << resources.GetErrorMessage();
     auto& res = *resources;
     ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U);
-    ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
                                 R::system_overlay_invalid::string::not_overlayable,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::other,
                                 R::system_overlay_invalid::string::other, false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
                                 R::system_overlay_invalid::string::policy_actor,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
                                 R::system_overlay_invalid::string::policy_odm,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
                                 R::system_overlay_invalid::string::policy_oem,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
                                 R::system_overlay_invalid::string::policy_product,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                                 R::system_overlay_invalid::string::policy_public,
                                 false /* rewrite */));
     ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
-                                Res_value::TYPE_REFERENCE,
                                 R::system_overlay_invalid::string::policy_config_signature,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
                                 R::system_overlay_invalid::string::policy_signature,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                                 R::system_overlay_invalid::string::policy_system,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(
-        res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-        R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                                R::system_overlay_invalid::string::policy_system_vendor,
+                                false /* rewrite */));
   };
 
   CheckEntries(PolicyFlags::SIGNATURE);
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
index 9641f6b..69575b8 100644
--- a/cmds/idmap2/tests/TestConstants.h
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -19,7 +19,7 @@
 
 namespace android::idmap2::TestConstants {
 
-constexpr const auto TARGET_CRC =  0x7c2d4719;
+constexpr const auto TARGET_CRC = 0x7c2d4719;
 constexpr const auto TARGET_CRC_STRING = "7c2d4719";
 
 constexpr const auto OVERLAY_CRC = 0x5afff726;
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index b599dcb..d0a8e3d 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
     0x49, 0x44, 0x4d, 0x50,
 
     // 0x4: version
-    0x04, 0x00, 0x00, 0x00,
+    0x05, 0x00, 0x00, 0x00,
 
     // 0x8: target crc
     0x34, 0x12, 0x00, 0x00,
@@ -42,9 +42,9 @@
     0x11, 0x00, 0x00, 0x00,
 
     // 0x14: enforce overlayable
-    0x01,
+    0x01, 0x00, 0x00, 0x00,
 
-    // 0x15: target path "targetX.apk"
+    // 0x18: target path "targetX.apk"
     0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -62,7 +62,7 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 
-    // 0x115: overlay path "overlayX.apk"
+    // 0x118: overlay path "overlayX.apk"
     0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -80,71 +80,89 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 
-    // 0x215: debug string
-    // string length, including terminating null
-    0x08, 0x00, 0x00, 0x00,
+    // 0x218: debug string
+    // string length,
+    0x05, 0x00, 0x00, 0x00,
 
-    // string contents "debug\0\0\0" (padded to word alignment)
+    // 0x21c string contents "debug\0\0\0" (padded to word alignment)
     0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
 
     // DATA HEADER
-    // 0x221: target_package_id
+    // 0x224: target_package_id
     0x7f,
 
-    // 0x222: overlay_package_id
+    // 0x225: overlay_package_id
     0x7f,
 
-    // 0x223: target_entry_count
+    // 0x226: padding
+    0x00, 0x00,
+
+    // 0x228: target_entry_count
     0x03, 0x00, 0x00, 0x00,
 
-    // 0x227: overlay_entry_count
+    // 0x22c: target_inline_entry_count
+    0x01, 0x00, 0x00, 0x00,
+
+    // 0x230: overlay_entry_count
     0x03, 0x00, 0x00, 0x00,
 
-    // 0x22b: string_pool_offset
-    0x00, 0x00, 0x00, 0x00,
-
-    // 0x22f: string_pool_byte_length
+    // 0x234: string_pool_offset
     0x00, 0x00, 0x00, 0x00,
 
     // TARGET ENTRIES
-    // 0x233: 0x7f020000
+    // 0x238: target id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x237: TYPE_REFERENCE
-    0x01,
-
-    // 0x238: 0x7f020000
+    // 0x23c: overlay_id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x23c: 0x7f030000
+    // 0x240: target id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x240: TYPE_REFERENCE
-    0x01,
-
-    // 0x241: 0x7f030000
+    // 0x244: overlay_id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x245: 0x7f030002
+    // 0x248: target id (0x7f030002)
     0x02, 0x00, 0x03, 0x7f,
 
-    // 0x249: TYPE_REFERENCE
-    0x01,
-
-    // 0x24a: 0x7f030001
+    // 0x24c: overlay_id (0x7f030001)
     0x01, 0x00, 0x03, 0x7f,
 
+    // INLINE TARGET ENTRIES
+
+    // 0x250: target_id
+    0x00, 0x00, 0x04, 0x7f,
+
+    // 0x254: Res_value::size (value ignored by idmap)
+    0x08, 0x00,
+
+    // 0x256: Res_value::res0 (value ignored by idmap)
+    0x00,
+
+    // 0x257: Res_value::dataType (TYPE_INT_HEX)
+    0x11,
+
+    // 0x258: Res_value::data
+    0x78, 0x56, 0x34, 0x12,
+
     // OVERLAY ENTRIES
-    // 0x24e: 0x7f020000 -> 0x7f020000
+    // 0x25c: 0x7f020000 -> 0x7f020000
     0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
 
-    // 0x256: 0x7f030000 -> 0x7f030000
+    // 0x264: 0x7f030000 -> 0x7f030000
     0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
 
-    // 0x25e: 0x7f030001 -> 0x7f030002
-    0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
+    // 0x26c: 0x7f030001 -> 0x7f030002
+    0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
 
-const unsigned int idmap_raw_data_len = 0x266;
+    // 0x274: string pool
+    // string length,
+    0x04, 0x00, 0x00, 0x00,
+
+    // 0x278 string contents "test" (padded to word alignment)
+    0x74, 0x65, 0x73, 0x74};
+
+const unsigned int idmap_raw_data_len = 0x27c;
 
 std::string GetTestDataPath();
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 88db1d8..012450d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -430,6 +430,30 @@
     },
 }
 
+java_library {
+    name: "statsdprotonano",
+    sdk_version: "9",
+    proto: {
+        type: "nano",
+        output_params: ["store_unknown_fields=true"],
+        include_dirs: ["external/protobuf/src"],
+    },
+    srcs: [
+        "src/atoms.proto",
+        "src/shell/shell_config.proto",
+        "src/shell/shell_data.proto",
+        "src/stats_log.proto",
+        "src/statsd_config.proto",
+    ],
+    static_libs: [
+        "platformprotosnano",
+    ],
+    // Protos have lots of MissingOverride and similar.
+    errorprone: {
+        javacflags: ["-XepDisableAllChecks"],
+    },
+}
+
 // Filegroup for statsd config proto definition.
 filegroup {
     name: "statsd-config-proto-def",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6eba5c6..aeafccb 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -497,13 +497,14 @@
         ModemRestart modem_restart = 312 [(module) = "telephony"];
         CarrierIdMismatchEvent carrier_id_mismatch_event = 313 [(module) = "telephony"];
         CarrierIdMatchingTable carrier_id_table_update = 314 [(module) = "telephony"];
+        DataStallRecoveryReported data_stall_recovery_reported = 315 [(module) = "telephony"];
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10089
+    // Next: 10090
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -605,6 +606,7 @@
         IncomingSms incoming_sms = 10086 [(module) = "telephony"];
         OutgoingSms outgoing_sms = 10087 [(module) = "telephony"];
         CarrierIdMatchingTable carrier_id_table_version = 10088 [(module) = "telephony"];
+        DataCallSession data_call_session = 10089 [(module) = "telephony"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -10672,6 +10674,104 @@
 }
 
 /**
+ * Pulls information for a single data call session
+ *
+ * Each pull creates multiple atoms, one for each data call session.
+ * The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message DataCallSession {
+    // A random number to be used as dimension to capture multiple atoms
+    optional int32 dimension = 1;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 2;
+
+    // Whether the call was made with an eSIM profile.
+    optional bool is_esim = 3;
+
+    // Data profile of this call (for what purpose this call was made)
+    optional android.telephony.DataProfileEnum profile = 4;
+
+    // APN type bitmask of the APN used:
+    // @ApnType in frameworks/base/telephony/java/android/telephony/Annotation.java.
+    optional int32 apn_type_bitmask = 5;
+
+    // Carrier ID of the SIM
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 6;
+
+    // Whether the subscription is roaming
+    optional bool is_roaming = 7;
+
+    // Data RAT when the call ended, can be IWLAN for IMS/MMS, otherwise should be WWAN PS RAT.
+    // In the case that the connection hasn't ended yet, this field holds the current RAT.
+    // In the case the call ended due to Out Of Service (OOS),
+    // this field should be the last known RAT.
+    optional android.telephony.NetworkTypeEnum rat_at_end = 8;
+
+    // Was the data call ended due to OOS
+    optional bool oos_at_end = 9;
+
+    // Number of RAT switches during the data call
+    optional int64 rat_switch_count = 10;
+
+    // Whether the call is on an opportunistic subscription
+    optional bool is_opportunistic = 11;
+
+    // Packet data protocol used
+    optional android.telephony.ApnProtocolEnum ip_type = 12;
+
+    // Whether the data call terminated before being established
+    optional bool setup_failed = 13;
+
+    // Reason why the data call terminated, as in RIL_DataCallFailCause from ril.h
+    optional int32 failure_cause = 14;
+
+    // Suggested retry back-off timer value from RIL
+    optional int32 suggested_retry_millis = 15;
+
+    // Why the data call was deactivated
+    // Set by telephony for MO deactivations (unrelated to failure_cause)
+    optional android.telephony.DataDeactivateReasonEnum deactivate_reason = 16;
+
+    // Duration of the data call, rounded into the closest 5 minutes.
+    optional int64 duration_minutes = 17;
+
+    // Whether the data call is still connected when the atom is collected.
+    optional bool ongoing = 18;
+}
+
+/**
+ * Logs data stall recovery event
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+ */
+message DataStallRecoveryReported {
+    // Carrier ID of the SIM
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 1;
+
+    // Data RAT when the stall happened
+    optional android.telephony.NetworkTypeEnum rat = 2;
+
+    // Signal strength when stall happened
+    optional android.telephony.SignalStrengthEnum signal_strength = 3;
+
+    // Action taken to recover
+    optional android.telephony.DataStallRecoveryActionEnum action = 4;
+
+    // Whether the subscription is opportunistic
+    optional bool is_opportunistic = 5;
+
+    // Whether the device is in multi-SIM mode
+    optional bool is_multi_sim = 6;
+}
+
+/**
  * Logs gnss stats from location service provider
  *
  * Pulled from:
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3acafaa..b2c0b32 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -17,14 +17,17 @@
 #define DEBUG false
 
 #include "Log.h"
+
 #include "DurationMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -64,8 +67,8 @@
 
 DurationMetricProducer::DurationMetricProducer(
         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
-        const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-        const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+        const vector<ConditionState>& initialConditionCache, const int startIndex,
+        const int stopIndex, const int stopAllIndex, const bool nesting,
         const sp<ConditionWizard>& wizard, const uint64_t protoHash,
         const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
@@ -143,6 +146,84 @@
     VLOG("~DurationMetric() called");
 }
 
+bool DurationMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const DurationMetric& metric = config.duration_metric(configIndex);
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the config");
+        return false;
+    }
+
+    const Predicate& durationWhat = config.predicate(what_it->second);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return false;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+
+    // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
+    // maps.
+    if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStartIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return false;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopIndex)) {
+        return false;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopAllIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+
+    for (const auto& it : mCurrentSlicedDurationTrackerMap) {
+        it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
+    }
+
+    return true;
+}
+
 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
         const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -550,7 +631,7 @@
     }
 
     // Handles Stopall events.
-    if (matcherIndex == mStopAllIndex) {
+    if ((int)matcherIndex == mStopAllIndex) {
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
             whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
         }
@@ -598,7 +679,7 @@
     }
 
     // Handles Stop events.
-    if (matcherIndex == mStopIndex) {
+    if ((int)matcherIndex == mStopIndex) {
         if (mUseWhatDimensionAsInternalDimension) {
             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 3a94d9c..01198a9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -40,8 +40,8 @@
 public:
     DurationMetricProducer(
             const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
-            const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-            const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+            const vector<ConditionState>& initialConditionCache, const int startIndex,
+            const int stopIndex, const int stopAllIndex, const bool nesting,
             const sp<ConditionWizard>& wizard, const uint64_t protoHash,
             const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
             const int64_t startTimeNs,
@@ -112,16 +112,32 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
     const DurationMetric_AggregationType mAggregationType;
 
     // Index of the SimpleAtomMatcher which defines the start.
-    const size_t mStartIndex;
+    int mStartIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop.
-    const size_t mStopIndex;
+    int mStopIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
-    const size_t mStopAllIndex;
+    int mStopAllIndex;
 
     // nest counting -- for the same key, stops must match the number of starts to make real stop
     const bool mNested;
@@ -167,6 +183,8 @@
                 TestSumDurationWithSplitInFollowingBucket);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 18e62d2..92c1a6e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -569,6 +569,7 @@
     FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
 };
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 8d59d13..657b2e4 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -89,6 +89,12 @@
 
     virtual ~DurationTracker(){};
 
+    void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+        sp<ConditionWizard> tmpWizard = mWizard;
+        mWizard = wizard;
+        mConditionTrackerIndex = conditionTrackerIndex;
+    };
+
     virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
                            const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -191,7 +197,7 @@
 
     sp<ConditionWizard> mWizard;
 
-    const int mConditionTrackerIndex;
+    int mConditionTrackerIndex;
 
     const int64_t mBucketSizeNs;
 
@@ -217,6 +223,8 @@
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index d32f5a9..cfc6e3f 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -697,6 +697,43 @@
         }
         newMetricProducers.push_back(producer.value());
     }
+    for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+        const DurationMetric& metric = config.duration_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                producer = createDurationMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                        allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
     for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
         newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
         const EventMetric& metric = config.event_metric(i);
@@ -770,7 +807,7 @@
         }
         newMetricProducers.push_back(producer.value());
     }
-    // TODO: perform update for value, duration metric.
+    // TODO: perform update for value metric.
 
     const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
                                           config.whitelisted_atom_ids().end());
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 34e265c..b7dc2c7 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -365,7 +365,7 @@
         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
-        ALOGW("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
+        ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
         return nullopt;
     }
     int trackerIndex;
@@ -423,6 +423,125 @@
                                     eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
 }
 
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
+              (long long)metric.id());
+        return nullopt;
+    }
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the condition trackers");
+        return nullopt;
+    }
+
+    const Predicate& durationWhat = config.predicate(what_it->second);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return nullopt;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+    bool nesting = simplePredicate.count_nesting();
+
+    int startIndex = -1, stopIndex = -1, stopAllIndex = -1;
+    if (!simplePredicate.has_start() ||
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) {
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, stopAllIndex)) {
+        return nullopt;
+    }
+
+    FieldMatcher internalDimensions = simplePredicate.dimensions();
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else if (metric.links_size() > 0) {
+        ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+        return nullopt;
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+            ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+            return nullopt;
+        }
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else if (metric.state_link_size() > 0) {
+        ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+        return nullopt;
+    }
+
+    // Check that all metric state links are a subset of dimensions_in_what fields.
+    std::vector<Matcher> dimensionsInWhat;
+    translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+    for (const auto& stateLink : metric.state_link()) {
+        if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new DurationMetricProducer(
+            key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex,
+            nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs,
+            eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
 optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
@@ -438,7 +557,7 @@
         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
-        ALOGW("cannot find the metric name or what in config");
+        ALOGE("cannot find the metric name or what in config");
         return nullopt;
     }
     int trackerIndex;
@@ -497,7 +616,7 @@
         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
-        ALOGW("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+        ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
         return nullopt;
     }
 
@@ -760,114 +879,17 @@
         const DurationMetric& metric = config.duration_metric(i);
         metricMap.insert({metric.id(), metricIndex});
 
-        auto what_it = conditionTrackerMap.find(metric.what());
-        if (what_it == conditionTrackerMap.end()) {
-            ALOGE("DurationMetric's \"what\" is invalid");
-            return false;
-        }
-
-        const Predicate& durationWhat = config.predicate(what_it->second);
-
-        if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
-            ALOGE("DurationMetric's \"what\" must be a simple condition");
-            return false;
-        }
-
-        const auto& simplePredicate = durationWhat.simple_predicate();
-
-        bool nesting = simplePredicate.count_nesting();
-
-        int trackerIndices[3] = {-1, -1, -1};
-        if (!simplePredicate.has_start() ||
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[0])) {
-            ALOGE("Duration metrics must specify a valid the start event matcher");
-            return false;
-        }
-
-        if (simplePredicate.has_stop() &&
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[1])) {
-            return false;
-        }
-
-        if (simplePredicate.has_stop_all() &&
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[2])) {
-            return false;
-        }
-
-        FieldMatcher internalDimensions = simplePredicate.dimensions();
-
-        int conditionIndex = -1;
-
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
-                ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
-                return false;
-            }
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
-                return false;
-            }
-        }
-
-        // Check that all metric state links are a subset of dimensions_in_what fields.
-        std::vector<Matcher> dimensionsInWhat;
-        translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
-        for (const auto& stateLink : metric.state_link()) {
-            if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+        optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        uint64_t metricHash;
-        if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+                metricsWithActivation);
+        if (!producer) {
             return false;
         }
-
-        sp<MetricProducer> durationMetric = new DurationMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
-                trackerIndices[1], trackerIndices[2], nesting, wizard, metricHash,
-                internalDimensions, timeBaseTimeNs, currentTimeNs, eventActivationMap,
-                eventDeactivationMap, slicedStateAtoms, stateGroupMap);
-
-        allMetricProducers.push_back(durationMetric);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build EventMetricProducer
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index f909aff..6d1e6dd 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -112,6 +112,25 @@
         std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
         std::vector<int>& metricsWithActivation);
 
+// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
 // Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
 // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
 optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index a20be15..dc951be 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -27,6 +27,7 @@
 #include "src/condition/CombinationConditionTracker.h"
 #include "src/condition/SimpleConditionTracker.h"
 #include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/DurationMetricProducer.h"
 #include "src/metrics/GaugeMetricProducer.h"
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "tests/statsd_test_util.h"
@@ -2212,6 +2213,352 @@
     EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
 }
 
+TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    AtomMatcher matcher7 = CreateBatteryStateNoneMatcher();
+    int64_t matcher7Id = matcher7.id();
+    *config.add_atom_matcher() = matcher7;
+
+    AtomMatcher matcher8 = CreateBatteryStateUsbMatcher();
+    int64_t matcher8Id = matcher8.id();
+    *config.add_atom_matcher() = matcher8;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    Predicate predicate2 = CreateScreenIsOffPredicate();
+    int64_t predicate2Id = predicate2.id();
+    *config.add_predicate() = predicate2;
+
+    Predicate predicate3 = CreateDeviceUnpluggedPredicate();
+    int64_t predicate3Id = predicate3.id();
+    *config.add_predicate() = predicate3;
+
+    Predicate predicate4 = CreateIsInBackgroundPredicate();
+    *predicate4.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1});
+    int64_t predicate4Id = predicate4.id();
+    *config.add_predicate() = predicate4;
+
+    Predicate predicate5 = CreateHoldingWakelockPredicate();
+    *predicate5.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id);
+    int64_t predicate5Id = predicate5.id();
+    *config.add_predicate() = predicate5;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few duration metrics.
+    // Will be preserved.
+    DurationMetric duration1 =
+            createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id});
+    *duration1.mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    MetricConditionLink* link = duration1.add_links();
+    link->set_condition(predicate4Id);
+    *link->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *link->mutable_fields_in_condition() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    int64_t duration1Id = duration1.id();
+    *config.add_duration_metric() = duration1;
+
+    // Will be replaced.
+    DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {});
+    int64_t duration2Id = duration2.id();
+    *config.add_duration_metric() = duration2;
+
+    // Will be replaced.
+    DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id});
+    int64_t duration3Id = duration3.id();
+    *config.add_duration_metric() = duration3;
+
+    // Will be replaced.
+    DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {});
+    int64_t duration4Id = duration4.id();
+    *config.add_duration_metric() = duration4;
+
+    // Will be deleted.
+    DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {});
+    int64_t duration5Id = duration5.id();
+    *config.add_duration_metric() = duration5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Make some sliced conditions true.
+    int uid1 = 10;
+    int uid2 = 11;
+    vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
+    matchingStates[2] = kMatched;
+    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(5, false);
+    unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(oldConditionTrackers[4]->isSliced());
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    fill(changedCache.begin(), changedCache.end(), false);
+    event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    // The duration trackers have a pointer to the wizard, and 2 trackers were created above.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 8);
+
+    // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced.
+    set<int64_t> replacedConditions({predicate1Id, predicate2Id});
+    set<int64_t> replacedStates({state1Id});
+
+    // New duration metric.
+    DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {});
+    *duration6.mutable_dimensions_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    link = duration6.add_links();
+    link->set_condition(predicate5Id);
+    *link->mutable_fields_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    *link->mutable_fields_in_condition() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    int64_t duration6Id = duration6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3,
+              matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7;
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index},
+                                                                {matcher7Id, matcher7Index},
+                                                                {matcher6Id, matcher6Index},
+                                                                {matcher5Id, matcher5Index},
+                                                                {matcher4Id, matcher4Index},
+                                                                {matcher3Id, matcher3Index},
+                                                                {matcher2Id, matcher2Index},
+                                                                {matcher1Id, matcher1Index}});
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8);
+    reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                 newAtomMatchingTrackers.begin());
+
+    const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3,
+              predicate1Index = 4;
+    std::unordered_map<int64_t, int> newConditionTrackerMap({
+            {predicate5Id, predicate5Index},
+            {predicate4Id, predicate4Index},
+            {predicate3Id, predicate3Index},
+            {predicate2Id, predicate2Index},
+            {predicate1Id, predicate1Index},
+    });
+    // Use the existing conditionTrackers and reinitialize them to get the initial condition cache.
+    vector<sp<ConditionTracker>> newConditionTrackers(5);
+    reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                 newConditionTrackers.begin());
+    vector<Predicate> conditionProtos(5);
+    reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin());
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated(
+                conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap,
+                newConditionTrackerMap));
+    }
+    vector<bool> cycleTracker(5, false);
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers,
+                                                  newConditionTrackerMap, cycleTracker,
+                                                  conditionCache));
+    }
+    // Predicate5 should be true since 2 uids have wakelocks
+    EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown}));
+
+    StatsdConfig newConfig;
+    *newConfig.add_duration_metric() = duration6;
+    const int duration6Index = 0;
+    *newConfig.add_duration_metric() = duration3;
+    const int duration3Index = 1;
+    *newConfig.add_duration_metric() = duration1;
+    const int duration1Index = 2;
+    *newConfig.add_duration_metric() = duration4;
+    const int duration4Index = 3;
+    *newConfig.add_duration_metric() = duration2;
+    const int duration2Index = 4;
+
+    for (const Predicate& predicate : conditionProtos) {
+        *newConfig.add_predicate() = predicate;
+    }
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {duration1Id, duration1Index}, {duration2Id, duration2Index},
+            {duration3Id, duration3Index}, {duration4Id, duration4Index},
+            {duration6Id, duration6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
+              newMetricProducers[newMetricProducerMap.at(duration1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)],
+              newMetricProducers[newMetricProducerMap.at(duration2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)],
+              newMetricProducers[newMetricProducerMap.at(duration3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)],
+              newMetricProducers[newMetricProducerMap.at(duration4Id)]);
+
+    // Verify the conditionToMetricMap. Note that the "what" is not in this map.
+    ASSERT_EQ(conditionToMetricMap.size(), 3);
+    const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+    EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index));
+    const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index];
+    EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index];
+    EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index));
+
+    // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here.
+    ASSERT_EQ(trackerToMetricMap.size(), 8);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index];
+    EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index];
+    EXPECT_THAT(matcher7Metrics,
+                UnorderedElementsAre(duration1Index, duration3Index, duration4Index));
+    const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index];
+    EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions are correct.
+    DurationMetricProducer* durationProducer1 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get());
+    EXPECT_EQ(durationProducer1->getMetricId(), duration1Id);
+    EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index);
+    EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index);
+    EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index);
+    EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index);
+    EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2);
+    for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) {
+        EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index);
+    }
+    DurationMetricProducer* durationProducer2 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get());
+    EXPECT_EQ(durationProducer2->getMetricId(), duration2Id);
+    EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index);
+    EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index);
+    EXPECT_EQ(durationProducer2->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer3 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get());
+    EXPECT_EQ(durationProducer3->getMetricId(), duration3Id);
+    EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer3->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer4 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get());
+    EXPECT_EQ(durationProducer4->getMetricId(), duration4Id);
+    EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer4->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer6 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get());
+    EXPECT_EQ(durationProducer6->getMetricId(), duration6Id);
+    EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index);
+    // TODO(b/167491517): should this be unknown since the condition is sliced?
+    EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index);
+    EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index);
+    EXPECT_EQ(durationProducer6->mStopAllIndex, -1);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 8);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
     StatsdConfig config;
     // Add atom matchers
@@ -2376,11 +2723,16 @@
     int64_t gaugeMetricId = gaugeMetric.id();
     *config.add_gauge_metric() = gaugeMetric;
 
+    // Preserved.
+    DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
+    int64_t durationMetricId = durationMetric.id();
+    *config.add_duration_metric() = durationMetric;
+
     EXPECT_TRUE(initConfig(config));
 
     // Used later to ensure the condition wizard is replaced. Get it before doing the update.
     sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
-    EXPECT_EQ(oldConditionWizard->getStrongCount(), 4);
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 5);
 
     // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
     set<int64_t> replacedMatchers;
@@ -2414,10 +2766,15 @@
     StatsdConfig newConfig;
     *newConfig.add_count_metric() = countMetric;
     const int countMetricIndex = 0;
+    *newConfig.add_duration_metric() = durationMetric;
+    const int durationMetricIndex = 1;
     *newConfig.add_event_metric() = eventMetric;
-    const int eventMetricIndex = 1;
+    const int eventMetricIndex = 2;
     *newConfig.add_gauge_metric() = gaugeMetric;
-    const int gaugeMetricIndex = 2;
+    const int gaugeMetricIndex = 3;
+
+    // Add the predicate since duration metric needs it.
+    *newConfig.add_predicate() = predicate1;
 
     // Output data structures to validate.
     unordered_map<int64_t, int> newMetricProducerMap;
@@ -2440,15 +2797,18 @@
 
     unordered_map<int64_t, int> expectedMetricProducerMap = {
             {countMetricId, countMetricIndex},
+            {durationMetricId, durationMetricIndex},
             {eventMetricId, eventMetricIndex},
             {gaugeMetricId, gaugeMetricIndex},
     };
     EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
 
     // Make sure preserved metrics are the same.
-    ASSERT_EQ(newMetricProducers.size(), 3);
+    ASSERT_EQ(newMetricProducers.size(), 4);
     EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
               newMetricProducers[newMetricProducerMap.at(countMetricId)]);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
+              newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
 
     // Make sure replaced metrics are different.
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
@@ -2464,9 +2824,9 @@
     // Verify the trackerToMetricMap.
     ASSERT_EQ(trackerToMetricMap.size(), 3);
     const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
-    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex));
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex));
     const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
-    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex));
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
     const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
     EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex));
 
@@ -2479,6 +2839,9 @@
     EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
     EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
     EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue);
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
@@ -2488,7 +2851,7 @@
 
     sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
     EXPECT_NE(newConditionWizard, oldConditionWizard);
-    EXPECT_EQ(newConditionWizard->getStrongCount(), 4);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 5);
     oldMetricProducers.clear();
     // Only reference to the old wizard should be the one in the test.
     EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 28bd540..4ea2aac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2791,7 +2791,7 @@
                         memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalPrivateClean(),
                         memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
-                        memInfo.getTotalSwappedOut(), memInfo.getTotalPss(),
+                        memInfo.getTotalSwappedOut(), memInfo.getTotalRss(),
                         nativeMax+dalvikMax,
                         nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
             }
@@ -3631,7 +3631,7 @@
             TAG, "Handling launch of " + r);
 
         // Initialize before creating the activity
-        if (!ThreadedRenderer.sRendererDisabled
+        if (ThreadedRenderer.sRendererEnabled
                 && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
             HardwareRenderer.preload();
         }
@@ -7417,14 +7417,7 @@
 
     @UnsupportedAppUsage
     public static ActivityThread systemMain() {
-        // The system process on low-memory devices do not get to use hardware
-        // accelerated drawing, since this can add too much overhead to the
-        // process.
-        if (!ActivityManager.isHighEndGfx()) {
-            ThreadedRenderer.disable(true);
-        } else {
-            ThreadedRenderer.enableForegroundTrimming();
-        }
+        ThreadedRenderer.initForSystemProcess();
         ActivityThread thread = new ActivityThread();
         thread.attach(true, 0);
         return thread;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5438062..60e7f0b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8457,9 +8457,7 @@
                 Action action, StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             container.setViewVisibility(buttonId, View.VISIBLE);
-            if (buttonId != R.id.media_seamless) {
-                container.setImageViewIcon(buttonId, action.getIcon());
-            }
+            container.setImageViewIcon(buttonId, action.getIcon());
 
             // If the action buttons should not be tinted, then just use the default
             // notification color. Otherwise, just use the passed-in color.
@@ -8513,10 +8511,6 @@
                     view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
             }
-            bindMediaActionButton(view, R.id.media_seamless, new Action(
-                    R.drawable.ic_media_seamless, mBuilder.mContext.getString(
-                            com.android.internal.R.string.ext_media_seamless_action), null), p);
-            view.setViewVisibility(R.id.media_seamless, View.GONE);
             handleImage(view);
             // handle the content margin
             int endMargin = R.dimen.notification_content_margin_end;
@@ -8553,10 +8547,6 @@
                     big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
             }
-            bindMediaActionButton(big, R.id.media_seamless, new Action(R.drawable.ic_media_seamless,
-                    mBuilder.mContext.getString(
-                            com.android.internal.R.string.ext_media_seamless_action), null), p);
-            big.setViewVisibility(R.id.media_seamless, View.GONE);
             handleImage(big);
             return big;
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c4157cf..bbc1406 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -382,6 +382,7 @@
      * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
      * @hide
      */
+    @SystemApi
     public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;
 
     /**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ea4b762..b371141 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1419,6 +1419,13 @@
     public static final class WindowLayout {
         public WindowLayout(int width, float widthFraction, int height, float heightFraction,
                 int gravity, int minWidth, int minHeight) {
+            this(width, widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+                    null /* windowLayoutAffinity */);
+        }
+
+        /** @hide */
+        public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+                int gravity, int minWidth, int minHeight, String windowLayoutAffinity) {
             this.width = width;
             this.widthFraction = widthFraction;
             this.height = height;
@@ -1426,6 +1433,7 @@
             this.gravity = gravity;
             this.minWidth = minWidth;
             this.minHeight = minHeight;
+            this.windowLayoutAffinity = windowLayoutAffinity;
         }
 
         /** @hide */
@@ -1506,6 +1514,8 @@
         /**
          * Affinity of window layout parameters. Activities with the same UID and window layout
          * affinity will share the same window dimension record.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_windowLayoutAffinity
          * @hide
          */
         public String windowLayoutAffinity;
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index d9ecf46..389458b 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -21,9 +21,9 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.LocusId;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IOnAppsChangedListener;
-import android.content.pm.LauncherActivityInfoInternal;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutQueryWrapper;
 import android.content.pm.IPackageInstallerCallback;
@@ -47,7 +47,7 @@
     void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
     ParceledListSlice getLauncherActivities(
             String callingPackage, String packageName, in UserHandle user);
-    LauncherActivityInfoInternal resolveLauncherActivityInternal(
+    ActivityInfo resolveActivity(
             String callingPackage, in ComponentName component, in UserHandle user);
     void startSessionDetailsActivityAsUser(in IApplicationThread caller, String callingPackage,
                 String callingFeatureId, in PackageInstaller.SessionInfo sessionInfo,
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index f24ed80..fcb1de0 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -33,5 +33,4 @@
             in Bundle launcherExtras);
     void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
     void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
-    void onPackageProgressChanged(in UserHandle user, String packageName, float progress);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c32d344..30f3325 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageLoadingProgressCallback;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.IntentFilterVerificationInfo;
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index ead80d0..67deb82 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -34,19 +35,28 @@
     private static final String TAG = "LauncherActivityInfo";
 
     private final PackageManager mPm;
+
+    @UnsupportedAppUsage
+    private ActivityInfo mActivityInfo;
+    private ComponentName mComponentName;
     private UserHandle mUser;
-    private final LauncherActivityInfoInternal mInternal;
 
     /**
      * Create a launchable activity object for a given ResolveInfo and user.
      *
      * @param context The context for fetching resources.
-
+     * @param info ResolveInfo from which to create the LauncherActivityInfo.
+     * @param user The UserHandle of the profile to which this activity belongs.
      */
-    LauncherActivityInfo(Context context, UserHandle user, LauncherActivityInfoInternal internal) {
-        mPm = context.getPackageManager();
+    LauncherActivityInfo(Context context, ActivityInfo info, UserHandle user) {
+        this(context);
+        mActivityInfo = info;
+        mComponentName =  new ComponentName(info.packageName, info.name);
         mUser = user;
-        mInternal = internal;
+    }
+
+    LauncherActivityInfo(Context context) {
+        mPm = context.getPackageManager();
     }
 
     /**
@@ -55,7 +65,7 @@
      * @return ComponentName of the activity
      */
     public ComponentName getComponentName() {
-        return mInternal.getComponentName();
+        return mComponentName;
     }
 
     /**
@@ -80,28 +90,7 @@
      */
     public CharSequence getLabel() {
         // TODO: Go through LauncherAppsService
-        return mInternal.getActivityInfo().loadLabel(mPm);
-    }
-
-    /**
-     * @return whether the package is startable.
-     */
-    public boolean isStartable() {
-        return mInternal.getIncrementalStatesInfo().isStartable();
-    }
-
-    /**
-     * @return whether the package is still loading.
-     */
-    public boolean isLoading() {
-        return mInternal.getIncrementalStatesInfo().isLoading();
-    }
-
-    /**
-     * @return Package loading progress
-     */
-    public float getProgress() {
-        return mInternal.getIncrementalStatesInfo().getProgress();
+        return mActivityInfo.loadLabel(mPm);
     }
 
     /**
@@ -114,20 +103,20 @@
      */
     public Drawable getIcon(int density) {
         // TODO: Go through LauncherAppsService
-        final int iconRes = mInternal.getActivityInfo().getIconResource();
+        final int iconRes = mActivityInfo.getIconResource();
         Drawable icon = null;
         // Get the preferred density icon from the app's resources
         if (density != 0 && iconRes != 0) {
             try {
-                final Resources resources = mPm.getResourcesForApplication(
-                        mInternal.getActivityInfo().applicationInfo);
+                final Resources resources
+                        = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
                 icon = resources.getDrawableForDensity(iconRes, density);
             } catch (NameNotFoundException | Resources.NotFoundException exc) {
             }
         }
         // Get the default density icon
         if (icon == null) {
-            icon = mInternal.getActivityInfo().loadIcon(mPm);
+            icon = mActivityInfo.loadIcon(mPm);
         }
         return icon;
     }
@@ -139,7 +128,7 @@
      * @hide remove before shipping
      */
     public int getApplicationFlags() {
-        return mInternal.getActivityInfo().flags;
+        return mActivityInfo.applicationInfo.flags;
     }
 
     /**
@@ -147,7 +136,7 @@
      * @return
      */
     public ApplicationInfo getApplicationInfo() {
-        return mInternal.getActivityInfo().applicationInfo;
+        return mActivityInfo.applicationInfo;
     }
 
     /**
@@ -158,7 +147,7 @@
     public long getFirstInstallTime() {
         try {
             // TODO: Go through LauncherAppsService
-            return mPm.getPackageInfo(mInternal.getActivityInfo().packageName,
+            return mPm.getPackageInfo(mActivityInfo.packageName,
                     PackageManager.MATCH_UNINSTALLED_PACKAGES).firstInstallTime;
         } catch (NameNotFoundException nnfe) {
             // Sorry, can't find package
@@ -171,7 +160,7 @@
      * @return the name from android:name for the acitivity.
      */
     public String getName() {
-        return mInternal.getActivityInfo().name;
+        return mActivityInfo.name;
     }
 
     /**
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.aidl b/core/java/android/content/pm/LauncherActivityInfoInternal.aidl
deleted file mode 100644
index 5d98d54..0000000
--- a/core/java/android/content/pm/LauncherActivityInfoInternal.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2020, 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.content.pm;
-
-parcelable LauncherActivityInfoInternal;
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.java b/core/java/android/content/pm/LauncherActivityInfoInternal.java
deleted file mode 100644
index 839b176..0000000
--- a/core/java/android/content/pm/LauncherActivityInfoInternal.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2020 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.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- */
-public class LauncherActivityInfoInternal implements Parcelable {
-    @UnsupportedAppUsage
-    private ActivityInfo mActivityInfo;
-    private ComponentName mComponentName;
-    @NonNull private IncrementalStatesInfo mIncrementalStatesInfo;
-
-    /**
-     * @param info ActivityInfo from which to create the LauncherActivityInfo.
-     * @param incrementalStatesInfo The package's states.
-     */
-    public LauncherActivityInfoInternal(ActivityInfo info,
-            @Nullable IncrementalStatesInfo incrementalStatesInfo) {
-        mActivityInfo = info;
-        mComponentName = new ComponentName(info.packageName, info.name);
-        if (incrementalStatesInfo == null) {
-            // default value for non-incremental apps
-            mIncrementalStatesInfo = new IncrementalStatesInfo(
-                    true /* isStartable */, false /* isLoading */, 1 /* progress */);
-        } else {
-            mIncrementalStatesInfo = incrementalStatesInfo;
-        }
-    }
-
-    public LauncherActivityInfoInternal(Parcel source) {
-        mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader());
-        mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
-        mIncrementalStatesInfo = source.readParcelable(
-                IncrementalStatesInfo.class.getClassLoader());
-    }
-
-    public ComponentName getComponentName() {
-        return mComponentName;
-    }
-
-    public ActivityInfo getActivityInfo() {
-        return mActivityInfo;
-    }
-
-    public IncrementalStatesInfo getIncrementalStatesInfo() {
-        return mIncrementalStatesInfo;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mActivityInfo, 0);
-        dest.writeParcelable(mIncrementalStatesInfo, 0);
-    }
-
-    public static final @android.annotation.NonNull Creator<LauncherActivityInfoInternal> CREATOR =
-            new Creator<LauncherActivityInfoInternal>() {
-        public LauncherActivityInfoInternal createFromParcel(Parcel source) {
-            return new LauncherActivityInfoInternal(source);
-        }
-        public LauncherActivityInfoInternal[] newArray(int size) {
-            return new LauncherActivityInfoInternal[size];
-        }
-    };
-}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index b7af397..1a694b3 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -218,7 +218,6 @@
          * Indicates that a package was modified in the specified profile.
          * This can happen, for example, when the package is updated or when
          * one or more components are enabled or disabled.
-         * It can also happen if package state has changed, i.e., package becomes unstartable.
          *
          * @param packageName The name of the package that has changed.
          * @param user The UserHandle of the profile that generated the change.
@@ -324,16 +323,6 @@
         public void onShortcutsChanged(@NonNull String packageName,
                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
         }
-
-        /**
-         * Indicates that the loading progress of an installed package has changed.
-         *
-         * @param packageName The name of the package that has changed.
-         * @param user The UserHandle of the profile that generated the change.
-         * @param progress The new progress value, between [0, 1].
-         */
-        public void onPackageProgressChanged(@NonNull String packageName,
-                @NonNull UserHandle user, float progress) {}
     }
 
     /**
@@ -726,15 +715,16 @@
     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
         logErrorForInvalidProfileAccess(user);
         try {
-            LauncherActivityInfoInternal ai = mService.resolveLauncherActivityInternal(
-                    mContext.getPackageName(), intent.getComponent(), user);
-            if (ai == null) {
-                return null;
+            ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(),
+                    intent.getComponent(), user);
+            if (ai != null) {
+                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
+                return info;
             }
-            return new LauncherActivityInfo(mContext, user, ai);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
+        return null;
     }
 
     /**
@@ -823,13 +813,13 @@
     }
 
     private List<LauncherActivityInfo> convertToActivityList(
-            @Nullable ParceledListSlice<LauncherActivityInfoInternal> internals, UserHandle user) {
-        if (internals == null || internals.getList().isEmpty()) {
+            @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
+        if (activities == null) {
             return Collections.EMPTY_LIST;
         }
         ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
-        for (LauncherActivityInfoInternal internal : internals.getList()) {
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, user, internal);
+        for (ResolveInfo ri : activities.getList()) {
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
             if (DEBUG) {
                 Log.v(TAG, "Returning activity for profile " + user + " : "
                         + lai.getComponentName());
@@ -1677,19 +1667,6 @@
                 }
             }
         }
-
-        public void onPackageProgressChanged(UserHandle user, String packageName,
-                float progress) {
-            if (DEBUG) {
-                Log.d(TAG, "onPackageProgressChanged " + user.getIdentifier() + ","
-                        + packageName + "," + progress);
-            }
-            synchronized (LauncherApps.this) {
-                for (CallbackMessageHandler callback : mCallbacks) {
-                    callback.postOnPackageProgressChanged(user, packageName, progress);
-                }
-            }
-        }
     };
 
     private static class CallbackMessageHandler extends Handler {
@@ -1701,7 +1678,6 @@
         private static final int MSG_SUSPENDED = 6;
         private static final int MSG_UNSUSPENDED = 7;
         private static final int MSG_SHORTCUT_CHANGED = 8;
-        private static final int MSG_LOADING_PROGRESS_CHANGED = 9;
 
         private LauncherApps.Callback mCallback;
 
@@ -1712,7 +1688,6 @@
             boolean replacing;
             UserHandle user;
             List<ShortcutInfo> shortcuts;
-            float mLoadingProgress;
         }
 
         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
@@ -1752,10 +1727,6 @@
                 case MSG_SHORTCUT_CHANGED:
                     mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
                     break;
-                case MSG_LOADING_PROGRESS_CHANGED:
-                    mCallback.onPackageProgressChanged(info.packageName, info.user,
-                            info.mLoadingProgress);
-                    break;
             }
         }
 
@@ -1822,15 +1793,6 @@
             info.shortcuts = shortcuts;
             obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
         }
-
-        public void postOnPackageProgressChanged(UserHandle user, String packageName,
-                float progress) {
-            CallbackInfo info = new CallbackInfo();
-            info.packageName = packageName;
-            info.user = user;
-            info.mLoadingProgress = progress;
-            obtainMessage(MSG_LOADING_PROGRESS_CHANGED, info).sendToTarget();
-        }
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 8c0bfef..511ee5d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -368,8 +368,8 @@
                 }
                 result = intentResult;
             } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
-                ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
-                        input);
+                ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                        parseActivityWindowLayout(resources, parser, input);
                 if (layoutResult.isSuccess()) {
                     activity.windowLayout = layoutResult.getResult();
                 }
@@ -383,7 +383,8 @@
             }
         }
 
-        ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+        ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                resolveActivityWindowLayout(activity, input);
         if (layoutResult.isError()) {
             return input.error(layoutResult);
         }
@@ -468,7 +469,7 @@
     }
 
     @NonNull
-    private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res,
+    private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res,
             AttributeSet attrs, ParseInput input) {
         TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
         try {
@@ -496,8 +497,13 @@
             int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
             int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
                     -1);
-            return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height,
-                    heightFraction, gravity, minWidth, minHeight));
+            String windowLayoutAffinity =
+                    sw.getNonConfigurationString(
+                            R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0);
+            final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width,
+                    widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+                    windowLayoutAffinity);
+            return input.success(windowLayout);
         } finally {
             sw.recycle();
         }
@@ -509,7 +515,7 @@
      * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
      * Android R and some variants of pre-R.
      */
-    private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+    private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
             ParsedActivity activity, ParseInput input) {
         // There isn't a metadata for us to fall back. Whatever is in layout is correct.
         if (activity.metaData == null || !activity.metaData.containsKey(
@@ -528,9 +534,10 @@
         if (layout == null) {
             layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
                     -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
-                    -1 /* minWidth */, -1 /* minHeight */);
+                    -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity);
+        } else {
+            layout.windowLayoutAffinity = windowLayoutAffinity;
         }
-        layout.windowLayoutAffinity = windowLayoutAffinity;
         return input.success(layout);
     }
 }
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index a6e8c13..0f3cdfc 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -616,7 +616,7 @@
     public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
 
     /**
-     * A constant describing a motion detect sensor.
+     * A constant describing a heart beat sensor.
      *
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      *
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9906331..236fab0 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -550,7 +550,7 @@
      *   <h4>{@link android.hardware.Sensor#TYPE_HEART_BEAT
      * Sensor.TYPE_HEART_BEAT}:</h4>
      *
-     * A sensor of this type returns an event everytime a hear beat peak is
+     * A sensor of this type returns an event everytime a heart beat peak is
      * detected.
      *
      * Peak here ideally corresponds to the positive peak in the QRS complex of
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index c5f8dac..2aefb1d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -626,10 +626,10 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
+    public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
         if (mService != null) try {
             mGenerateChallengeCallback = callback;
-            mService.generateChallenge(mToken, sensorId, mServiceReceiver,
+            mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
                     mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -642,7 +642,7 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void generateChallenge(GenerateChallengeCallback callback) {
+    public void generateChallenge(int userId, GenerateChallengeCallback callback) {
         final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
                 getSensorPropertiesInternal();
         if (fingerprintSensorProperties.isEmpty()) {
@@ -651,7 +651,7 @@
         }
 
         final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-        generateChallenge(sensorId, callback);
+        generateChallenge(sensorId, userId, callback);
     }
 
     /**
@@ -659,10 +659,10 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void revokeChallenge() {
+    public void revokeChallenge(int userId) {
         // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
         // this parameter is ignored.
-        revokeChallenge(0L);
+        revokeChallenge(userId, 0L);
     }
 
     /**
@@ -670,9 +670,17 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void revokeChallenge(long challenge) {
+    public void revokeChallenge(int userId, long challenge) {
         if (mService != null) try {
-            mService.revokeChallenge(mToken, mContext.getOpPackageName(), challenge);
+            final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
+                    getSensorPropertiesInternal();
+            if (fingerprintSensorProperties.isEmpty()) {
+                Slog.e(TAG, "No sensors");
+                return;
+            }
+            final int sensorId = fingerprintSensorProperties.get(0).sensorId;
+            mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
+                    challenge);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index cc086cf..9566837 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -93,10 +93,10 @@
     boolean isHardwareDetected(String opPackageName);
 
     // Get a pre-enrollment authentication token
-    void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
+    void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName);
 
     // Finish an enrollment sequence and invalidate the authentication token
-    void revokeChallenge(IBinder token, String opPackageName, long challenge);
+    void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge);
 
     // Determine if a user has at least one enrolled fingerprint
     boolean hasEnrolledFingerprints(int userId, String opPackageName);
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 38d9883..a4f7b74 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,6 +28,12 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * This class represents a single algorithm that can be used by an {@link IpSecTransform}.
@@ -52,6 +59,27 @@
     public static final String CRYPT_AES_CBC = "cbc(aes)";
 
     /**
+     * AES-CTR Encryption/Ciphering Algorithm.
+     *
+     * <p>Valid lengths for keying material are {160, 224, 288}.
+     *
+     * <p>As per <a href="https://tools.ietf.org/html/rfc3686#section-5.1">RFC3686 (Section
+     * 5.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
+     * nonce. RFC compliance requires that the nonce must be unique per security association.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
+
+    /**
      * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
@@ -99,6 +127,25 @@
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
     /**
+     * AES-XCBC Authentication/Integrity Algorithm.
+     *
+     * <p>Keys for this algorithm must be 128 bits in length.
+     *
+     * <p>The only valid truncation length is 96 bits.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String AUTH_AES_XCBC = "xcbc(aes)";
+
+    /**
      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for keying material are {160, 224, 288}.
@@ -111,19 +158,67 @@
      */
     public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
 
+    /**
+     * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm.
+     *
+     * <p>Keys for this algorithm must be 288 bits in length.
+     *
+     * <p>As per <a href="https://tools.ietf.org/html/rfc7634#section-2">RFC7634 (Section 2)</a>,
+     * keying material consists of a 256 bit key followed by a 32-bit salt. The salt is fixed per
+     * security association.
+     *
+     * <p>The only valid ICV (truncation) length is 128 bits.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
+
     /** @hide */
     @StringDef({
         CRYPT_AES_CBC,
+        CRYPT_AES_CTR,
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
         AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
-        AUTH_CRYPT_AES_GCM
+        AUTH_AES_XCBC,
+        AUTH_CRYPT_AES_GCM,
+        AUTH_CRYPT_CHACHA20_POLY1305
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AlgorithmName {}
 
+    /** @hide */
+    @VisibleForTesting
+    public static final Map<String, Integer> ALGO_TO_REQUIRED_FIRST_SDK = new HashMap<>();
+
+    static {
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CBC, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_MD5, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA1, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA256, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA384, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, Build.VERSION_CODES.P);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, Build.VERSION_CODES.P);
+
+        // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+    }
+
+    private static final Set<String> ENABLED_ALGOS =
+            Collections.unmodifiableSet(loadAlgos(Resources.getSystem()));
+
     private final String mName;
     private final byte[] mKey;
     private final int mTruncLenBits;
@@ -137,6 +232,7 @@
      *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
+     * @throws IllegalArgumentException if algorithm or key length is invalid.
      */
     public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
         this(algorithm, key, 0);
@@ -152,6 +248,7 @@
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      * @param truncLenBits number of bits of output hash to use.
+     * @throws IllegalArgumentException if algorithm, key length or truncation length is invalid.
      */
     public IpSecAlgorithm(
             @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
@@ -206,13 +303,59 @@
                 }
             };
 
-    private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
-        boolean isValidLen = true;
-        boolean isValidTruncLen = true;
+    /**
+     * Returns supported IPsec algorithms for the current device.
+     *
+     * <p>Some algorithms may not be supported on old devices. Callers MUST check if an algorithm is
+     * supported before using it.
+     */
+    @NonNull
+    public static Set<String> getSupportedAlgorithms() {
+        return ENABLED_ALGOS;
+    }
 
-        switch(name) {
+    /** @hide */
+    @VisibleForTesting
+    public static Set<String> loadAlgos(Resources systemResources) {
+        final Set<String> enabledAlgos = new HashSet<>();
+
+        // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
+        // the resource are not allowed.
+        final String[] resourceAlgos = systemResources.getStringArray(
+                com.android.internal.R.array.config_optionalIpSecAlgorithms);
+        for (String str : resourceAlgos) {
+            if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
+                // This error should be caught by CTS and never be thrown to API callers
+                throw new IllegalArgumentException("Invalid or repeated algorithm " + str);
+            }
+        }
+
+        for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) {
+            if (Build.VERSION.FIRST_SDK_INT >= entry.getValue()) {
+                enabledAlgos.add(entry.getKey());
+            }
+        }
+
+        return enabledAlgos;
+    }
+
+    private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
+        final boolean isValidLen;
+        final boolean isValidTruncLen;
+
+        if (!getSupportedAlgorithms().contains(name)) {
+            throw new IllegalArgumentException("Unsupported algorithm: " + name);
+        }
+
+        switch (name) {
             case CRYPT_AES_CBC:
                 isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
+                isValidTruncLen = true;
+                break;
+            case CRYPT_AES_CTR:
+                // The keying material for AES-CTR is a key plus a 32-bit salt
+                isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = true;
                 break;
             case AUTH_HMAC_MD5:
                 isValidLen = keyLen == 128;
@@ -234,12 +377,22 @@
                 isValidLen = keyLen == 512;
                 isValidTruncLen = truncLen >= 256 && truncLen <= 512;
                 break;
+            case AUTH_AES_XCBC:
+                isValidLen = keyLen == 128;
+                isValidTruncLen = truncLen == 96;
+                break;
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
                 isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
+            case AUTH_CRYPT_CHACHA20_POLY1305:
+                // The keying material for ChaCha20Poly1305 is a key plus a 32-bit salt
+                isValidLen = keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 128;
+                break;
             default:
+                // Should never hit here.
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
         }
 
@@ -260,6 +413,7 @@
             case AUTH_HMAC_SHA256:
             case AUTH_HMAC_SHA384:
             case AUTH_HMAC_SHA512:
+            case AUTH_AES_XCBC:
                 return true;
             default:
                 return false;
@@ -268,12 +422,24 @@
 
     /** @hide */
     public boolean isEncryption() {
-        return getName().equals(CRYPT_AES_CBC);
+        switch (getName()) {
+            case CRYPT_AES_CBC: // fallthrough
+            case CRYPT_AES_CTR:
+                return true;
+            default:
+                return false;
+        }
     }
 
     /** @hide */
     public boolean isAead() {
-        return getName().equals(AUTH_CRYPT_AES_GCM);
+        switch (getName()) {
+            case AUTH_CRYPT_AES_GCM: // fallthrough
+            case AUTH_CRYPT_CHACHA20_POLY1305:
+                return true;
+            default:
+                return false;
+        }
     }
 
     // Because encryption keys are sensitive and userdebug builds are used by large user pools
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 555d710..7cb3f92 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -22,7 +22,6 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.util.LinkPropertiesUtils;
-import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1669,78 +1668,6 @@
     }
 
     /**
-     * Compares the DNS addresses in this LinkProperties with another
-     * LinkProperties, examining only DNS addresses on the base link.
-     *
-     * @param target a LinkProperties with the new list of dns addresses
-     * @return the differences between the DNS addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<InetAddress> compareDnses(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the InetAddresses into removed, we will be removing
-         * dns address which are common between mDnses and target
-         * leaving the addresses that are different. And dns address which
-         * are in target but not in mDnses are placed in the
-         * addedAddresses.
-         */
-        return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
-    }
-
-    /**
-     * Compares the validated private DNS addresses in this LinkProperties with another
-     * LinkProperties.
-     *
-     * @param target a LinkProperties with the new list of validated private dns addresses
-     * @return the differences between the DNS addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<InetAddress> compareValidatedPrivateDnses(
-            @Nullable LinkProperties target) {
-        return new CompareResult<>(mValidatedPrivateDnses,
-                target != null ? target.getValidatedPrivateDnsServers() : null);
-    }
-
-    /**
-     * Compares all routes in this LinkProperties with another LinkProperties,
-     * examining both the the base link and all stacked links.
-     *
-     * @param target a LinkProperties with the new list of routes
-     * @return the differences between the routes.
-     * @hide
-     */
-    public @NonNull CompareResult<RouteInfo> compareAllRoutes(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the RouteInfos into removed, we will be removing
-         * routes which are common between mRoutes and target
-         * leaving the routes that are different. And route address which
-         * are in target but not in mRoutes are placed in added.
-         */
-        return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
-    }
-
-    /**
-     * Compares all interface names in this LinkProperties with another
-     * LinkProperties, examining both the the base link and all stacked links.
-     *
-     * @param target a LinkProperties with the new list of interface names
-     * @return the differences between the interface names.
-     * @hide
-     */
-    public @NonNull CompareResult<String> compareAllInterfaceNames(
-            @Nullable LinkProperties target) {
-        /*
-         * Duplicate the interface names into removed, we will be removing
-         * interface names which are common between this and target
-         * leaving the interface names that are different. And interface names which
-         * are in target but not in this are placed in added.
-         */
-        return new CompareResult<>(getAllInterfaceNames(),
-                target != null ? target.getAllInterfaceNames() : null);
-    }
-
-
-    /**
      * Generate hashcode based on significant fields
      *
      * Equal objects must produce the same hash code, while unequal objects
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index d492e08..fce3437 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -379,6 +379,7 @@
      *
      * @see #clearCallingIdentity
      */
+    @CriticalNative
     public static final native void restoreCallingIdentity(long token);
 
     /**
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 6ba1627..be21fea 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -109,11 +109,11 @@
     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
 
-    // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE
+    // Values for ANGLE_GL_DRIVER_ALL_ANGLE
     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
 
-    // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES
+    // Values for ANGLE_GL_DRIVER_SELECTION_VALUES
     private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
     private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
     private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
@@ -382,11 +382,11 @@
         final int allUseAngle;
         if (bundle != null) {
             allUseAngle =
-                    bundle.getInt(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+                    bundle.getInt(Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE);
         } else {
             ContentResolver contentResolver = context.getContentResolver();
             allUseAngle = Settings.Global.getInt(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                    Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
                     ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
         }
         if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
@@ -402,10 +402,10 @@
         final ContentResolver contentResolver = context.getContentResolver();
         final List<String> optInPackages =
                 getGlobalSettingsString(contentResolver, bundle,
-                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
+                        Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS);
         final List<String> optInValues =
                 getGlobalSettingsString(contentResolver, bundle,
-                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
+                        Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES);
 
         // Make sure we have good settings to use
         if (optInPackages.size() != optInValues.size()) {
@@ -462,11 +462,11 @@
 
         if (coreSettings != null) {
             debugPackage =
-                    coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+                    coreSettings.getString(Settings.Global.ANGLE_DEBUG_PACKAGE);
         } else {
             ContentResolver contentResolver = context.getContentResolver();
             debugPackage = Settings.Global.getString(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+                    Settings.Global.ANGLE_DEBUG_PACKAGE);
         }
         if (TextUtils.isEmpty(debugPackage)) {
             return "";
@@ -578,7 +578,7 @@
         final ContentResolver contentResolver = context.getContentResolver();
         final List<String> angleAllowlist =
                 getGlobalSettingsString(contentResolver, bundle,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST);
+                    Settings.Global.ANGLE_ALLOWLIST);
 
         if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
 
@@ -678,7 +678,7 @@
         try {
             ContentResolver contentResolver = context.getContentResolver();
             final int showDialogBox = Settings.Global.getInt(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX);
+                    Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX);
 
             return (showDialogBox == 1);
         } catch (Settings.SettingNotFoundException | SecurityException e) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 68d8d82..2eee643 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12367,37 +12367,34 @@
          * to dumpable apps that opt-in.
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE =
-                "angle_debug_package";
+        public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package";
 
         /**
          * Force all PKGs to use ANGLE, regardless of any other settings
          * The value is a boolean (1 or 0).
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE =
-                "angle_gl_driver_all_angle";
+        public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle";
 
         /**
          * List of PKGs that have an OpenGL driver selected
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS =
+        public static final String ANGLE_GL_DRIVER_SELECTION_PKGS =
                 "angle_gl_driver_selection_pkgs";
 
         /**
          * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES =
+        public static final String ANGLE_GL_DRIVER_SELECTION_VALUES =
                 "angle_gl_driver_selection_values";
 
         /**
          * List of package names that should check ANGLE rules
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_ALLOWLIST =
-                "angle_allowlist";
+        public static final String ANGLE_ALLOWLIST = "angle_allowlist";
 
         /**
          * Lists of ANGLE EGL features for debugging.
@@ -12413,8 +12410,7 @@
          * The value is a boolean (1 or 0).
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX =
-                "show_angle_in_use_dialog_box";
+        public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box";
 
         /**
          * Updatable driver global preference for all Apps.
@@ -14551,6 +14547,16 @@
         public static final String SHOW_PEOPLE_SPACE = "show_people_space";
 
         /**
+         * Which types of conversations to show in People Space.
+         * Values are:
+         * 0: All conversations (default)
+         * 1: Priority conversations only
+         * @hide
+         */
+        public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
+                "people_space_conversation_type";
+
+        /**
          * Whether to show new lockscreen & AOD UI.
          * Values are:
          * 0: Disabled (default)
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 8f8e6cc..b94031a 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -379,16 +379,23 @@
      * Callbacks for always-on hotword detection.
      */
     public static abstract class Callback {
+
         /**
-         * Called when the hotword availability changes.
-         * This indicates a change in the availability of recognition for the given keyphrase.
-         * It's called at least once with the initial availability.<p/>
+         * Updates the availability state of the active keyphrase and locale on every keyphrase
+         * sound model change.
          *
-         * Availability implies whether the hardware on this system is capable of listening for
-         * the given keyphrase or not. <p/>
+         * <p>This API is called whenever there's a possibility that the keyphrase associated
+         * with this detector has been updated. It is not guaranteed that there is in fact any
+         * change, as it may be called for other reasons.</p>
+         *
+         * <p>This API is also guaranteed to be called right after an AlwaysOnHotwordDetector
+         * instance is created to updated the current availability state.</p>
+         *
+         * <p>Availability implies the current enrollment state of the given keyphrase. If the
+         * hardware on this system is not capable of listening for the given keyphrase,
+         * {@link AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE} will be returned.
          *
          * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
-         * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
          */
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4d1337b..6a70a85 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -877,9 +877,10 @@
                         InputChannel inputChannel = new InputChannel();
 
                         if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
-                                mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets,
-                                mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel,
-                                mInsetsState, mTempControls) < 0) {
+                                mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
+                                mWinFrames.contentInsets, mWinFrames.stableInsets,
+                                mWinFrames.displayCutout, inputChannel, mInsetsState,
+                                mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index d441e6b..72b35b9 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2080,6 +2080,94 @@
     }
 
     /**
+     * Simple alternative to {@link String#format} which purposefully supports
+     * only a small handful of substitutions to improve execution speed.
+     * Benchmarking reveals this optimized alternative performs 6.5x faster for
+     * a typical format string.
+     * <p>
+     * Below is a summary of the limited grammar supported by this method; if
+     * you need advanced features, please continue using {@link String#format}.
+     * <ul>
+     * <li>{@code %b} for {@code boolean}
+     * <li>{@code %c} for {@code char}
+     * <li>{@code %d} for {@code int} or {@code long}
+     * <li>{@code %f} for {@code float} or {@code double}
+     * <li>{@code %s} for {@code String}
+     * <li>{@code %x} for hex representation of {@code int} or {@code long}
+     * <li>{@code %%} for literal {@code %}
+     * </ul>
+     *
+     * @throws IllegalArgumentException if the format string or arguments don't
+     *             match the supported grammar described above.
+     * @hide
+     */
+    public static @NonNull String formatSimple(@NonNull String format, Object... args) {
+        final StringBuilder sb = new StringBuilder(format);
+        int j = 0;
+        for (int i = 0; i < sb.length(); ) {
+            if (sb.charAt(i) == '%') {
+                final String repl;
+                final char code = sb.charAt(i + 1);
+                switch (code) {
+                    case 'b': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        if (arg instanceof Boolean) {
+                            repl = Boolean.toString((boolean) arg);
+                        } else {
+                            repl = Boolean.toString(arg != null);
+                        }
+                        break;
+                    }
+                    case 'c':
+                    case 'd':
+                    case 'f':
+                    case 's': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        repl = String.valueOf(arg);
+                        break;
+                    }
+                    case 'x': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        if (arg instanceof Integer) {
+                            repl = Integer.toHexString((int) arg);
+                        } else if (arg instanceof Long) {
+                            repl = Long.toHexString((long) arg);
+                        } else {
+                            throw new IllegalArgumentException(
+                                    "Unsupported hex type " + arg.getClass());
+                        }
+                        break;
+                    }
+                    case '%': {
+                        repl = "%";
+                        break;
+                    }
+                    default: {
+                        throw new IllegalArgumentException("Unsupported format code " + code);
+                    }
+                }
+                sb.replace(i, i + 2, repl);
+                i += repl.length();
+            } else {
+                i++;
+            }
+        }
+        if (j != args.length) {
+            throw new IllegalArgumentException("Too many arguments");
+        }
+        return sb.toString();
+    }
+
+    /**
      * Returns whether or not the specified spanned text has a style span.
      * @hide
      */
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 910fd90..7f36169 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -45,12 +45,13 @@
  */
 interface IWindowSession {
     int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
-            in int viewVisibility, in int layerStackId, out Rect outFrame,
-            out Rect outContentInsets, out Rect outStableInsets,
+            in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
+            out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
             out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls);
     int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
                 in int viewVisibility, in int layerStackId, in int userId,
+                in InsetsState requestedVisibility,
                 out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
                 out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
                 out InsetsState insetsState, out InsetsSourceControl[] activeControls);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 71899fa..878583b 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -212,21 +212,21 @@
     /**
      * @return Whether the finish callback of this animation should be invoked.
      */
-    public boolean applyChangeInsets(InsetsState state) {
+    public boolean applyChangeInsets(@Nullable InsetsState outState) {
         if (mCancelled) {
             if (DEBUG) Log.d(TAG, "applyChangeInsets canceled");
             return false;
         }
         final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
         ArrayList<SurfaceParams> params = new ArrayList<>();
-        updateLeashesForSide(ISIDE_LEFT, offset.left, mShownInsets.left, mPendingInsets.left,
-                params, state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_TOP, offset.top, mShownInsets.top, mPendingInsets.top, params,
-                state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_RIGHT, offset.right, mShownInsets.right, mPendingInsets.right,
-                params, state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mShownInsets.bottom,
-                mPendingInsets.bottom, params, state, mPendingAlpha);
+        updateLeashesForSide(ISIDE_LEFT, offset.left, mPendingInsets.left, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_TOP, offset.top, mPendingInsets.top, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_RIGHT, offset.right, mPendingInsets.right, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, outState,
+                mPendingAlpha);
 
         mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
@@ -357,8 +357,8 @@
         return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
     }
 
-    private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int maxInset,
-            int inset, ArrayList<SurfaceParams> surfaceParams, InsetsState state, Float alpha) {
+    private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset,
+            ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) {
         ArraySet<InsetsSourceControl> items = mSideSourceMap.get(side);
         if (items == null) {
             return;
@@ -377,8 +377,10 @@
                     ? (mAnimationType == ANIMATION_TYPE_SHOW ? true : !mFinished)
                     : inset != 0;
 
-            state.getSource(source.getType()).setVisible(visible);
-            state.getSource(source.getType()).setFrame(mTmpFrame);
+            if (outState != null) {
+                outState.getSource(source.getType()).setVisible(visible);
+                outState.getSource(source.getType()).setFrame(mTmpFrame);
+            }
 
             // If the system is controlling the insets source, the leash can be null.
             if (leash != null) {
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index cc3cd27..1307052 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -44,7 +44,6 @@
     private final InsetsAnimationControlImpl mControl;
     private final InsetsAnimationControlCallbacks mOuterCallbacks;
     private final Handler mMainThreadHandler;
-    private final InsetsState mState = new InsetsState();
     private final InsetsAnimationControlCallbacks mCallbacks =
             new InsetsAnimationControlCallbacks() {
 
@@ -60,7 +59,7 @@
 
         @Override
         public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
-            mControl.applyChangeInsets(mState);
+            mControl.applyChangeInsets(null /* outState */);
         }
 
         @Override
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 652781a..5037d9e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -484,7 +484,9 @@
     /** The state dispatched from server */
     private final InsetsState mLastDispatchedState = new InsetsState();
 
-    /** The state sent to server */
+    // TODO: Use other class to represent the requested visibility of each type, because the
+    //       display frame and the frame in each source are not used.
+    /** The requested visibilities sent to server */
     private final InsetsState mRequestedState = new InsetsState();
 
     private final Rect mFrame = new Rect();
@@ -499,6 +501,7 @@
     private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims =
             Collections.unmodifiableList(mTmpRunningAnims);
     private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
+    private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>();
     private WindowInsets mLastInsets;
 
     private boolean mAnimCallbackScheduled;
@@ -640,11 +643,6 @@
             if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
             mHost.notifyInsetsChanged();
         }
-        if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */,
-                true /* excludeInvisibleIme */)) {
-            if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
-            updateRequestedState();
-        }
         return true;
     }
 
@@ -808,9 +806,9 @@
         if (hideTypes[0] != 0) {
             applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
         }
-        if (requestedStateStale) {
-            updateRequestedState();
-        }
+
+        // InsetsSourceConsumer#setControl might change the requested visibility.
+        updateRequestedVisibility();
     }
 
     @Override
@@ -945,6 +943,7 @@
         if (types == 0) {
             // nothing to animate.
             listener.onCancelled(null);
+            updateRequestedVisibility();
             if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
             return;
         }
@@ -980,12 +979,14 @@
                     }
                 });
             }
+            updateRequestedVisibility();
             return;
         }
 
         if (typesReady == 0) {
             if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
             listener.onCancelled(null);
+            updateRequestedVisibility();
             return;
         }
 
@@ -1010,6 +1011,7 @@
         } else {
             hideDirectly(types, false /* animationFinished */, animationType);
         }
+        updateRequestedVisibility();
     }
 
     /**
@@ -1177,7 +1179,6 @@
         }
         if (stateChanged) {
             mHost.notifyInsetsChanged();
-            updateRequestedState();
         }
     }
 
@@ -1202,7 +1203,6 @@
     @VisibleForTesting
     public void notifyVisibilityChanged() {
         mHost.notifyInsetsChanged();
-        updateRequestedState();
     }
 
     /**
@@ -1251,38 +1251,39 @@
         return ANIMATION_TYPE_NONE;
     }
 
+    @VisibleForTesting
+    public void onRequestedVisibilityChanged(InsetsSourceConsumer consumer) {
+        mRequestedVisibilityChanged.add(consumer);
+    }
+
     /**
-     * Sends the local visibility state back to window manager if it is changed.
+     * Sends the requested visibilities to window manager if any of them is changed.
      */
-    private void updateRequestedState() {
+    private void updateRequestedVisibility() {
         boolean changed = false;
-        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
-            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+        for (int i = mRequestedVisibilityChanged.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mRequestedVisibilityChanged.valueAt(i);
             final @InternalInsetsType int type = consumer.getType();
             if (type == ITYPE_CAPTION_BAR) {
                 continue;
             }
-            if (consumer.getControl() != null) {
-                final InsetsSource localSource = mState.getSource(type);
-                if (!localSource.equals(mRequestedState.peekSource(type))) {
-                    // Our requested state is stale. Update it here and send it to window manager.
-                    mRequestedState.addSource(new InsetsSource(localSource));
-                    changed = true;
-                }
-                if (!localSource.equals(mLastDispatchedState.peekSource(type))) {
-                    // The server state is not what we expected. This can happen while we don't have
-                    // the control. Since we have the control now, we need to send our request again
-                    // to modify the server state.
-                    changed = true;
-                }
+            final boolean requestedVisible = consumer.isRequestedVisible();
+            if (requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type)) {
+                mRequestedState.getSource(type).setVisible(requestedVisible);
+                changed = true;
             }
         }
+        mRequestedVisibilityChanged.clear();
         if (!changed) {
             return;
         }
         mHost.onInsetsModified(mRequestedState);
     }
 
+    InsetsState getRequestedVisibility() {
+        return mRequestedState;
+    }
+
     @VisibleForTesting
     public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
         if (types == 0) {
@@ -1316,6 +1317,7 @@
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
         }
+        updateRequestedVisibility();
     }
 
     private void showDirectly(@InsetsType int types) {
@@ -1326,6 +1328,7 @@
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
         }
+        updateRequestedVisibility();
     }
 
     /**
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index d7ceaf7..e4a24eb 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -350,6 +350,7 @@
         if (mRequestedVisible != requestedVisible) {
             mRequestedVisible = requestedVisible;
             mIsAnimationPending = false;
+            mController.onRequestedVisibilityChanged(this);
             if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
         }
         if (applyLocalVisibilityOverride()) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 38441d1..b9f1f6a 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -63,8 +63,6 @@
  */
 public class InsetsState implements Parcelable {
 
-    public static final InsetsState EMPTY = new InsetsState();
-
     /**
      * Internal representation of inset source types. This is different from the public API in
      * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 0c3d61f..8b0de08 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -28,7 +28,6 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
@@ -54,7 +53,6 @@
     private OnClickListener mExpandClickListener;
     private OnClickListener mFeedbackListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
-    private LinearLayout mTransferChip;
     private NotificationExpandButton mExpandButton;
     private CachingIconView mIcon;
     private View mProfileBadge;
@@ -111,7 +109,6 @@
         mAppName = findViewById(com.android.internal.R.id.app_name_text);
         mHeaderText = findViewById(com.android.internal.R.id.header_text);
         mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
-        mTransferChip = findViewById(com.android.internal.R.id.media_seamless);
         mExpandButton = findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
@@ -143,8 +140,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
-                    || child == mFeedbackIcon
-                    || child == mTransferChip) {
+                    || child == mFeedbackIcon) {
                 iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
             } else {
                 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -208,8 +204,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
-                    || child == mFeedbackIcon
-                    || child == mTransferChip) {
+                    || child == mFeedbackIcon) {
                 if (end == getMeasuredWidth()) {
                     layoutRight = end - mContentEndMargin;
                 } else {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7b6a4f8..432d927 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1849,7 +1849,7 @@
         // If developers explicitly set the important mode for it, don't change the mode.
         // Only change the mode to important when this SurfaceView isn't explicitly set and has
         // an embedded hierarchy.
-        if (!mRemoteAccessibilityController.connected()
+        if ((mRemoteAccessibilityController!= null && !mRemoteAccessibilityController.connected())
                 || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             return mode;
         }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 14a324d..57ca71a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.HardwareRenderer;
@@ -26,7 +27,6 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
@@ -186,37 +186,12 @@
     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
     public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
 
-    static {
-        // Try to check OpenGL support early if possible.
-        isAvailable();
-    }
-
-    /**
-     * A process can set this flag to false to prevent the use of threaded
-     * rendering.
-     *
-     * @hide
-     */
-    public static boolean sRendererDisabled = false;
-
     /**
      * Further threaded renderer disabling for the system process.
      *
      * @hide
      */
-    public static boolean sSystemRendererDisabled = false;
-
-    /**
-     * Invoke this method to disable threaded rendering in the current process.
-     *
-     * @hide
-     */
-    public static void disable(boolean system) {
-        sRendererDisabled = true;
-        if (system) {
-            sSystemRendererDisabled = true;
-        }
-    }
+    public static boolean sRendererEnabled = true;
 
     public static boolean sTrimForeground = false;
 
@@ -230,16 +205,19 @@
         sTrimForeground = true;
     }
 
-
     /**
-     * Indicates whether threaded rendering is available under any form for
-     * the view hierarchy.
-     *
-     * @return True if the view hierarchy can potentially be defer rendered,
-     *         false otherwise
+     * Initialize HWUI for being in a system process like system_server
+     * Should not be called in non-system processes
      */
-    public static boolean isAvailable() {
-        return true;
+    public static void initForSystemProcess() {
+        // The system process on low-memory devices do not get to use hardware
+        // accelerated drawing, since this can add too much overhead to the
+        // process.
+        if (!ActivityManager.isHighEndGfx()) {
+            sRendererEnabled = false;
+        } else {
+            enableForegroundTrimming();
+        }
     }
 
     /**
@@ -250,11 +228,7 @@
      * @return A threaded renderer backed by OpenGL.
      */
     public static ThreadedRenderer create(Context context, boolean translucent, String name) {
-        ThreadedRenderer renderer = null;
-        if (isAvailable()) {
-            renderer = new ThreadedRenderer(context, translucent, name);
-        }
-        return renderer;
+        return new ThreadedRenderer(context, translucent, name);
     }
 
     private static final String[] VISUALIZERS = {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4f05a59..cf5ca56 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23573,8 +23573,7 @@
         if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
         if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
         if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
-        if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
-                ThreadedRenderer.isAvailable()) {
+        if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) {
             // This is set if HW acceleration is requested, even if the current
             // process doesn't allow it.  This is just to allow app preview
             // windows to better match their app.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5235740..80f5c0f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1021,8 +1021,10 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     adjustLayoutParamsForCompatibility(mWindowAttributes);
+                    controlInsetsForCompatibility(mWindowAttributes);
                     res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame,
+                            getHostVisibility(), mDisplay.getDisplayId(), userId,
+                            mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mDisplayCutout, inputChannel,
                             mTempInsets, mTempControls);
@@ -1291,10 +1293,6 @@
                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
 
         if (hardwareAccelerated) {
-            if (!ThreadedRenderer.isAvailable()) {
-                return;
-            }
-
             // Persistent processes (including the system) should not do
             // accelerated rendering on low-end devices.  In that case,
             // sRendererDisabled will be set.  In addition, the system process
@@ -1314,8 +1312,7 @@
                 // shows for launching applications, so they will look more like
                 // the app being launched.
                 mAttachInfo.mHardwareAccelerationRequested = true;
-            } else if (!ThreadedRenderer.sRendererDisabled
-                    || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
+            } else if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
                 if (mAttachInfo.mThreadedRenderer != null) {
                     mAttachInfo.mThreadedRenderer.destroy();
                 }
@@ -3881,10 +3878,6 @@
     }
 
     private void addFrameCallbackIfNeeded() {
-        if (!mNextDrawUseBLASTSyncTransaction) {
-            return;
-        }
-
         // Frame callbacks will always occur after submitting draw requests and before
         // the draw actually occurs. This will ensure that we set the next transaction
         // for the frame that's about to get drawn and not on a previous frame that.
@@ -3892,8 +3885,19 @@
         // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
         // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
         // next frame completed should be reported with the blast sync transaction.
-        registerRtFrameCallback(createFrameDrawingCallback());
-        mNextDrawUseBLASTSyncTransaction = false;
+        if (mNextDrawUseBLASTSyncTransaction) {
+            registerRtFrameCallback(createFrameDrawingCallback());
+            mNextDrawUseBLASTSyncTransaction = false;
+        } else if (mReportNextDraw) {
+            registerRtFrameCallback(frame -> {
+                if (mBlastBufferQueue != null) {
+                    // If we need to report next draw, wait for adapter to flush its shadow queue
+                    // by processing previously queued buffers so that we can submit the
+                    // transaction a timely manner.
+                    mBlastBufferQueue.flushShadowQueue();
+                }
+            });
+        }
     }
 
     private void performDraw() {
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 90a80ce..8f58df4 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -144,7 +144,9 @@
     @Override
     public void onInsetsModified(InsetsState insetsState) {
         try {
-            mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
+            if (mViewRoot.mAdded) {
+                mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call insetsModified", e);
         }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8490f2a..f01cbcc 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -527,7 +527,7 @@
             }
             allViewsRemoved = mRoots.isEmpty();
         }
-        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground) {
             doTrimForeground();
         }
 
@@ -561,29 +561,28 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void trimMemory(int level) {
-        if (ThreadedRenderer.isAvailable()) {
-            if (shouldDestroyEglContext(level)) {
-                // Destroy all hardware surfaces and resources associated to
-                // known windows
-                synchronized (mLock) {
-                    for (int i = mRoots.size() - 1; i >= 0; --i) {
-                        mRoots.get(i).destroyHardwareResources();
-                    }
+
+        if (shouldDestroyEglContext(level)) {
+            // Destroy all hardware surfaces and resources associated to
+            // known windows
+            synchronized (mLock) {
+                for (int i = mRoots.size() - 1; i >= 0; --i) {
+                    mRoots.get(i).destroyHardwareResources();
                 }
-                // Force a full memory flush
-                level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             }
+            // Force a full memory flush
+            level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+        }
 
-            ThreadedRenderer.trimMemory(level);
+        ThreadedRenderer.trimMemory(level);
 
-            if (ThreadedRenderer.sTrimForeground) {
-                doTrimForeground();
-            }
+        if (ThreadedRenderer.sTrimForeground) {
+            doTrimForeground();
         }
     }
 
     public static void trimForeground() {
-        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground) {
             WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
             wm.doTrimForeground();
         }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0c221ed..8147873 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -130,8 +130,8 @@
      */
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
-            Rect outStableInsets,
+            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+            Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -166,11 +166,11 @@
      */
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, int userId, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
+            int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+            Rect outFrame, Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return addToDisplay(window, attrs, viewVisibility, displayId,
+        return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
                 outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
                 outInsetsState, outActiveControls);
     }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index eaa738d..f2955ac 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -206,6 +206,10 @@
         int TEXT_LINK = 2;
     }
 
+    // Default content insertion handler.
+    private final TextViewOnReceiveContentCallback mDefaultOnReceiveContentCallback =
+            new TextViewOnReceiveContentCallback();
+
     // Each Editor manages its own undo stack.
     private final UndoManager mUndoManager = new UndoManager();
     private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
@@ -584,6 +588,11 @@
         mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
     }
 
+    @VisibleForTesting
+    public @NonNull TextViewOnReceiveContentCallback getDefaultOnReceiveContentCallback() {
+        return mDefaultOnReceiveContentCallback;
+    }
+
     /**
      * Forgets all undo and redo operations for this Editor.
      */
@@ -709,6 +718,8 @@
 
         hideCursorAndSpanControllers();
         stopTextActionModeWithPreservingSelection();
+
+        mDefaultOnReceiveContentCallback.clearInputConnectionInfo();
     }
 
     private void discardTextDisplayLists() {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2eadb56..b8a3249 100755
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1695,7 +1695,7 @@
         Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
         queryIntent.setComponent(searchActivity);
         PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
-                PendingIntent.FLAG_ONE_SHOT);
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
 
         // Now set up the bundle that will be inserted into the pending intent
         // when it's time to do the search.  We always build it here (even if empty)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 52a3f41..7bb2b7e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,6 +80,7 @@
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -890,13 +891,6 @@
     @UnsupportedAppUsage
     private Editor mEditor;
 
-    /**
-     * The default content insertion callback used by {@link TextView}. See
-     * {@link #setOnReceiveContentCallback} for more info.
-     */
-    private static final TextViewOnReceiveContentCallback DEFAULT_ON_RECEIVE_CONTENT_CALLBACK =
-            new TextViewOnReceiveContentCallback();
-
     private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
     private static final int DEVICE_PROVISIONED_NO = 1;
     private static final int DEVICE_PROVISIONED_YES = 2;
@@ -13723,6 +13717,23 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public void onInputConnectionOpenedInternal(@NonNull InputConnection ic,
+            @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+        if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().setInputConnectionInfo(ic, editorInfo);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void onInputConnectionClosedInternal() {
+        if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().clearInputConnectionInfo();
+        }
+    }
+
     /**
      * Returns the callback used for handling insertion of content into this view. See
      * {@link #setOnReceiveContentCallback} for more info.
@@ -13773,8 +13784,8 @@
         ClipDescription description = payload.getClip().getDescription();
         if (receiver != null && receiver.supports(this, description)) {
             receiver.onReceiveContent(this, payload);
-        } else {
-            DEFAULT_ON_RECEIVE_CONTENT_CALLBACK.onReceiveContent(this, payload);
+        } else if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload);
         }
     }
 
diff --git a/core/java/android/widget/TextViewOnReceiveContentCallback.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java
index 35618cb..d7c95b7 100644
--- a/core/java/android/widget/TextViewOnReceiveContentCallback.java
+++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java
@@ -16,24 +16,44 @@
 
 package android.widget;
 
+import static android.content.ContentResolver.SCHEME_CONTENT;
 import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
 import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
 import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
 
+import static java.util.Collections.singleton;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.OnReceiveContentCallback;
 import android.view.OnReceiveContentCallback.Payload.Flags;
 import android.view.OnReceiveContentCallback.Payload.Source;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
 
-import java.util.Collections;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -46,19 +66,26 @@
 public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
     private static final String LOG_TAG = "OnReceiveContent";
 
-    private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*");
+    private static final String MIME_TYPE_ALL_TEXT = "text/*";
+    private static final Set<String> MIME_TYPES_ALL_TEXT = singleton(MIME_TYPE_ALL_TEXT);
+
+    @Nullable private InputConnectionInfo mInputConnectionInfo;
+    @Nullable private ArraySet<String> mCachedSupportedMimeTypes;
 
     @SuppressLint("CallbackMethodName")
     @NonNull
     @Override
     public Set<String> getSupportedMimeTypes(@NonNull TextView view) {
-        return MIME_TYPES_ALL_TEXT;
+        if (!isUsageOfImeCommitContentEnabled(view)) {
+            return MIME_TYPES_ALL_TEXT;
+        }
+        return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes();
     }
 
     @Override
     public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
         if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
-            Log.d(LOG_TAG, "onReceive:" + payload);
+            Log.d(LOG_TAG, "onReceive: " + payload);
         }
         ClipData clip = payload.getClip();
         @Source int source = payload.getSource();
@@ -109,13 +136,22 @@
         editable.replace(start, end, replacement);
     }
 
-    private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
+    private boolean onReceiveForAutofill(@NonNull TextView view, @NonNull ClipData clip,
             @Flags int flags) {
-        final CharSequence text = coerceToText(clip, textView.getContext(), flags);
+        if (isUsageOfImeCommitContentEnabled(view)) {
+            clip = handleNonTextViaImeCommitContent(clip);
+            if (clip == null) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: Handled via IME");
+                }
+                return true;
+            }
+        }
+        final CharSequence text = coerceToText(clip, view.getContext(), flags);
         // First autofill it...
-        textView.setText(text);
+        view.setText(text);
         // ...then move cursor to the end.
-        final Editable editable = (Editable) textView.getText();
+        final Editable editable = (Editable) view.getText();
         Selection.setSelection(editable, editable.length());
         return true;
     }
@@ -146,4 +182,250 @@
         }
         return ssb;
     }
+
+    /**
+     * On Android S and above, the platform can provide non-text suggestions (e.g. images) via the
+     * augmented autofill framework (see
+     * <a href="/guide/topics/text/autofill-services">autofill services</a>). In order for an app to
+     * be able to handle these suggestions, it must normally implement the
+     * {@link android.view.OnReceiveContentCallback} API. To make the adoption of this smoother for
+     * apps that have previously implemented the
+     * {@link android.view.inputmethod.InputConnection#commitContent(InputContentInfo, int, Bundle)}
+     * API, we reuse that API as a fallback if {@link android.view.OnReceiveContentCallback} is not
+     * yet implemented by the app. This fallback is only enabled on Android S. This change ID
+     * disables the fallback, such that apps targeting Android T and above must implement the
+     * {@link android.view.OnReceiveContentCallback} API in order to accept non-text suggestions.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Enabled on Android T and higher
+    private static final long AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK = 163400105L;
+
+    /**
+     * Returns true if we can use the IME {@link InputConnection#commitContent} API in order handle
+     * non-text content.
+     */
+    private static boolean isUsageOfImeCommitContentEnabled(@NonNull View view) {
+        if (view.getOnReceiveContentCallback() != null) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "Fallback to commitContent disabled (custom callback is set)");
+            }
+            return false;
+        }
+        if (Compatibility.isChangeEnabled(AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK)) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "Fallback to commitContent disabled (target SDK is above S)");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private static final class InputConnectionInfo {
+        @NonNull private final WeakReference<InputConnection> mInputConnection;
+        @NonNull private final String[] mEditorInfoContentMimeTypes;
+
+        private InputConnectionInfo(@NonNull InputConnection inputConnection,
+                @NonNull String[] editorInfoContentMimeTypes) {
+            mInputConnection = new WeakReference<>(inputConnection);
+            mEditorInfoContentMimeTypes = editorInfoContentMimeTypes;
+        }
+
+        @Override
+        public String toString() {
+            return "InputConnectionInfo{"
+                    + "mimeTypes=" + Arrays.toString(mEditorInfoContentMimeTypes)
+                    + ", ic=" + mInputConnection
+                    + '}';
+        }
+    }
+
+    /**
+     * Invoked by the platform when an {@link InputConnection} is successfully created for the view
+     * that owns this callback instance.
+     */
+    void setInputConnectionInfo(@NonNull InputConnection ic, @NonNull EditorInfo editorInfo) {
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "setInputConnectionInfo: "
+                    + Arrays.toString(editorInfo.contentMimeTypes));
+        }
+        String[] contentMimeTypes = editorInfo.contentMimeTypes;
+        if (contentMimeTypes == null || contentMimeTypes.length == 0) {
+            mInputConnectionInfo = null;
+        } else {
+            mInputConnectionInfo = new InputConnectionInfo(ic, contentMimeTypes);
+        }
+    }
+
+    /**
+     * Invoked by the platform when an {@link InputConnection} is closed for the view that owns this
+     * callback instance.
+     */
+    void clearInputConnectionInfo() {
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "clearInputConnectionInfo: " + mInputConnectionInfo);
+        }
+        mInputConnectionInfo = null;
+    }
+
+    private Set<String> getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes() {
+        InputConnectionInfo icInfo = mInputConnectionInfo;
+        if (icInfo == null) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "getSupportedMimeTypes: No usable EditorInfo/InputConnection");
+            }
+            return MIME_TYPES_ALL_TEXT;
+        }
+        String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "getSupportedMimeTypes: Augmenting with EditorInfo.contentMimeTypes: "
+                    + Arrays.toString(editorInfoContentMimeTypes));
+        }
+        ArraySet<String> supportedMimeTypes = mCachedSupportedMimeTypes;
+        if (canReuse(supportedMimeTypes, editorInfoContentMimeTypes)) {
+            return supportedMimeTypes;
+        }
+        supportedMimeTypes = new ArraySet<>(editorInfoContentMimeTypes);
+        supportedMimeTypes.add(MIME_TYPE_ALL_TEXT);
+        mCachedSupportedMimeTypes = supportedMimeTypes;
+        return supportedMimeTypes;
+    }
+
+    /**
+     * We want to avoid creating a new set on every invocation of {@link #getSupportedMimeTypes}.
+     * This method will check if the cached set of MIME types matches the data in the given array
+     * from {@link EditorInfo} or if a new set should be created. The custom logic is needed for
+     * comparing the data because the set contains the additional "text/*" MIME type.
+     *
+     * @param cachedMimeTypes Previously cached set of MIME types.
+     * @param newEditorInfoMimeTypes MIME types from {@link EditorInfo}.
+     *
+     * @return Returns true if the data in the given cached set matches the data in the array.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean canReuse(@Nullable ArraySet<String> cachedMimeTypes,
+            @NonNull String[] newEditorInfoMimeTypes) {
+        if (cachedMimeTypes == null) {
+            return false;
+        }
+        if (newEditorInfoMimeTypes.length != cachedMimeTypes.size()
+                && newEditorInfoMimeTypes.length != (cachedMimeTypes.size() - 1)) {
+            return false;
+        }
+        final boolean ignoreAllTextMimeType =
+                newEditorInfoMimeTypes.length == (cachedMimeTypes.size() - 1);
+        for (String mimeType : cachedMimeTypes) {
+            if (ignoreAllTextMimeType && mimeType.equals(MIME_TYPE_ALL_TEXT)) {
+                continue;
+            }
+            boolean present = false;
+            for (String editorInfoContentMimeType : newEditorInfoMimeTypes) {
+                if (editorInfoContentMimeType.equals(mimeType)) {
+                    present = true;
+                    break;
+                }
+            }
+            if (!present) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Tries to insert the content in the clip into the app via the image keyboard API. If all the
+     * items in the clip are successfully inserted, returns null. If one or more of the items in the
+     * clip cannot be inserted, returns a non-null clip that contains the items that were not
+     * inserted.
+     */
+    @Nullable
+    private ClipData handleNonTextViaImeCommitContent(@NonNull ClipData clip) {
+        ClipDescription description = clip.getDescription();
+        if (!containsUri(clip) || containsOnlyText(clip)) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "onReceive: Clip doesn't contain any non-text URIs: "
+                        + description);
+            }
+            return clip;
+        }
+
+        InputConnectionInfo icInfo = mInputConnectionInfo;
+        InputConnection inputConnection = (icInfo != null) ? icInfo.mInputConnection.get() : null;
+        if (inputConnection == null) {
+            if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+                Log.d(LOG_TAG, "onReceive: No usable EditorInfo/InputConnection");
+            }
+            return clip;
+        }
+        String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
+        if (!isClipMimeTypeSupported(editorInfoContentMimeTypes, clip.getDescription())) {
+            if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+                Log.d(LOG_TAG,
+                        "onReceive: MIME type is not supported by the app's commitContent impl");
+            }
+            return clip;
+        }
+
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "onReceive: Trying to insert via IME: " + description);
+        }
+        ArrayList<ClipData.Item> remainingItems = new ArrayList<>(0);
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: No content URI in item: uri=" + uri);
+                }
+                remainingItems.add(item);
+                continue;
+            }
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "onReceive: Calling commitContent: uri=" + uri);
+            }
+            InputContentInfo contentInfo = new InputContentInfo(uri, description);
+            if (!inputConnection.commitContent(contentInfo, 0, null)) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: Call to commitContent returned false: uri=" + uri);
+                }
+                remainingItems.add(item);
+            }
+        }
+        if (remainingItems.isEmpty()) {
+            return null;
+        }
+        return new ClipData(description, remainingItems);
+    }
+
+    private static boolean isClipMimeTypeSupported(@NonNull String[] supportedMimeTypes,
+            @NonNull ClipDescription description) {
+        for (String imeSupportedMimeType : supportedMimeTypes) {
+            if (description.hasMimeType(imeSupportedMimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsUri(@NonNull ClipData clip) {
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            if (item.getUri() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsOnlyText(@NonNull ClipData clip) {
+        ClipDescription description = clip.getDescription();
+        for (int i = 0; i < description.getMimeTypeCount(); i++) {
+            String mimeType = description.getMimeType(i);
+            if (!mimeType.startsWith("text/")) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index 6b8cf63..c98477e 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -246,10 +246,12 @@
             }
             if (BRIGHTNESS_URI.equals(uri)) {
                 int currentBrightness = getScreenBrightnessInt(mContext);
+                mHandler.removeMessages(MSG_UPDATE_FLOAT);
                 mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
             } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
                 float currentFloat = getScreenBrightnessFloat(mContext);
                 int toSend = Float.floatToIntBits(currentFloat);
+                mHandler.removeMessages(MSG_UPDATE_INT);
                 mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
             }
         }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index e0f8a1a..cb4f285 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1655,18 +1655,24 @@
     private void showTargetDetails(DisplayResolveInfo ti) {
         if (ti == null) return;
 
-        List<DisplayResolveInfo> targetList;
+        ArrayList<DisplayResolveInfo> targetList;
 
         // For multiple targets, include info on all targets
         if (ti instanceof MultiDisplayResolveInfo) {
             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
             targetList = mti.getTargets();
         } else {
-            targetList = Collections.singletonList(ti);
+            targetList = new ArrayList<DisplayResolveInfo>();
+            targetList.add(ti);
         }
 
-        ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment(
-                targetList, mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+        ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
+        Bundle b = new Bundle();
+        b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+                mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+        b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+                targetList);
+        f.setArguments(b);
 
         f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
     }
@@ -1725,9 +1731,14 @@
         if (targetInfo instanceof MultiDisplayResolveInfo) {
             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
             if (!mti.hasSelected()) {
-                ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
-                        mti, which,
+                ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment();
+                Bundle b = new Bundle();
+                b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
                         mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+                b.putObject(ChooserStackedAppDialogFragment.MULTI_DRI_KEY,
+                        mti);
+                b.putInt(ChooserStackedAppDialogFragment.WHICH_KEY, which);
+                f.setArguments(b);
 
                 f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
                 return;
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
index fdeba8f..726622b 100644
--- a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -20,6 +20,7 @@
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -32,17 +33,26 @@
 public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogFragment
         implements DialogInterface.OnClickListener {
 
+    static final String WHICH_KEY = "which_key";
+    static final String MULTI_DRI_KEY = "multi_dri_key";
+
     private MultiDisplayResolveInfo mMultiDisplayResolveInfo;
     private int mParentWhich;
 
-    public ChooserStackedAppDialogFragment() {
+    public ChooserStackedAppDialogFragment() {}
+
+    void setStateFromBundle(Bundle b) {
+        mMultiDisplayResolveInfo = (MultiDisplayResolveInfo) b.get(MULTI_DRI_KEY);
+        mTargetInfos = mMultiDisplayResolveInfo.getTargets();
+        mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+        mParentWhich = b.getInt(WHICH_KEY);
     }
 
-    public ChooserStackedAppDialogFragment(MultiDisplayResolveInfo targets,
-            int parentWhich, UserHandle userHandle) {
-        super(targets.getTargets(), userHandle);
-        mMultiDisplayResolveInfo = targets;
-        mParentWhich = parentWhich;
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(WHICH_KEY, mParentWhich);
+        outState.putParcelable(MULTI_DRI_KEY, mMultiDisplayResolveInfo);
     }
 
     @Override
@@ -53,6 +63,7 @@
 
     @Override
     protected Drawable getItemIcon(DisplayResolveInfo dri) {
+
         // Show no icon for the group disambig dialog, null hides the imageview
         return null;
     }
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 21063d5..9afc0e9 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -32,7 +32,6 @@
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -59,25 +58,51 @@
 public class ChooserTargetActionsDialogFragment extends DialogFragment
         implements DialogInterface.OnClickListener {
 
-    protected List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
     protected UserHandle mUserHandle;
 
-    public ChooserTargetActionsDialogFragment() {
+    public static final String USER_HANDLE_KEY = "user_handle";
+    public static final String TARGET_INFOS_KEY = "target_infos";
+
+    public ChooserTargetActionsDialogFragment() {}
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            setStateFromBundle(savedInstanceState);
+        } else {
+            setStateFromBundle(getArguments());
+        }
     }
 
-    public ChooserTargetActionsDialogFragment(List<DisplayResolveInfo> targets,
-            UserHandle userHandle) {
-        mUserHandle = userHandle;
-        mTargetInfos = targets;
+    void setStateFromBundle(Bundle b) {
+        mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
+        mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+                mUserHandle);
+        outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+                mTargetInfos);
     }
 
     /**
      * Recreate the layout from scratch to match new Sharesheet redlines
      */
+    @Override
     public View onCreateView(LayoutInflater inflater,
             @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-
+        if (savedInstanceState != null) {
+            setStateFromBundle(savedInstanceState);
+        } else {
+            setStateFromBundle(getArguments());
+        }
         // Make the background transparent to show dialog rounding
         Optional.of(getDialog()).map(Dialog::getWindow)
                 .ifPresent(window -> {
@@ -204,12 +229,4 @@
                 mTargetInfos.get(0).getResolveInfo());
     }
 
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        // Dismiss on config changed (eg: rotation)
-        // TODO: Maintain state on config change
-        super.onConfigurationChanged(newConfig);
-        dismiss();
-    }
-
 }
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index fe0e7d0..b00148a 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -27,19 +27,22 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.UserHandle;
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * A TargetInfo plus additional information needed to render it (such as icon and label) and
  * resolve it to an activity.
  */
-public class DisplayResolveInfo implements TargetInfo {
+public class DisplayResolveInfo implements TargetInfo, Parcelable {
     // Temporary flag for new chooser delegate behavior. There are occassional token
     // permission errors from bouncing through the delegate. Watch out before reenabling:
     // b/157272342 is one example but this issue has been reported many times
@@ -202,4 +205,41 @@
         mPinned = pinned;
     }
 
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeCharSequence(mDisplayLabel);
+        dest.writeCharSequence(mExtendedInfo);
+        dest.writeParcelable(mResolvedIntent, 0);
+        dest.writeParcelableArray((Intent[]) mSourceIntents.toArray(), 0);
+        dest.writeBoolean(mIsSuspended);
+        dest.writeBoolean(mPinned);
+        dest.writeParcelable(mResolveInfo, 0);
+    }
+
+    public static final Parcelable.Creator<DisplayResolveInfo> CREATOR =
+            new Parcelable.Creator<DisplayResolveInfo>() {
+        public DisplayResolveInfo createFromParcel(Parcel in) {
+            return new DisplayResolveInfo(in);
+        }
+
+        public DisplayResolveInfo[] newArray(int size) {
+            return new DisplayResolveInfo[size];
+        }
+    };
+
+    private DisplayResolveInfo(Parcel in) {
+        mDisplayLabel = in.readCharSequence();
+        mExtendedInfo = in.readCharSequence();
+        mResolvedIntent = in.readParcelable(null /* ClassLoader */);
+        mSourceIntents.addAll(
+                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */)));
+        mIsSuspended = in.readBoolean();
+        mPinned = in.readBoolean();
+        mResolveInfo = in.readParcelable(null /* ClassLoader */);
+    }
 }
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
index cf921d7..2828e25 100644
--- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -23,14 +23,13 @@
 import com.android.internal.app.ResolverActivity;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Represents a "stack" of chooser targets for various activities within the same component.
  */
 public class MultiDisplayResolveInfo extends DisplayResolveInfo {
 
-    List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
     // We'll use this DRI for basic presentation info - eg icon, name.
     final DisplayResolveInfo mBaseInfo;
     // Index of selected target
@@ -61,7 +60,7 @@
     /**
      * List of all DisplayResolveInfos included in this target.
      */
-    public List<DisplayResolveInfo> getTargets() {
+    public ArrayList<DisplayResolveInfo> getTargets() {
         return mTargetInfos;
     }
 
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 9ba0259..670ca9f 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -28,7 +28,7 @@
 public class CompatibilityChangeInfo implements Parcelable {
     private final long mChangeId;
     private final @Nullable String mName;
-    private final int mEnableAfterTargetSdk;
+    private final int mEnableSinceTargetSdk;
     private final boolean mDisabled;
     private final boolean mLoggingOnly;
     private final @Nullable String mDescription;
@@ -42,8 +42,8 @@
         return mName;
     }
 
-    public int getEnableAfterTargetSdk() {
-        return mEnableAfterTargetSdk;
+    public int getEnableSinceTargetSdk() {
+        return mEnableSinceTargetSdk;
     }
 
     public boolean getDisabled() {
@@ -59,20 +59,37 @@
     }
 
     public CompatibilityChangeInfo(
-            Long changeId, String name, int enableAfterTargetSdk, boolean disabled,
-            boolean loggingOnly, String description) {
+            Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk,
+            boolean disabled, boolean loggingOnly, String description) {
         this.mChangeId = changeId;
         this.mName = name;
-        this.mEnableAfterTargetSdk = enableAfterTargetSdk;
+        if (enableAfterTargetSdk > 0) {
+            // Need to maintain support for @EnabledAfter(X), but make it equivalent to
+            // @EnabledSince(X+1)
+            this.mEnableSinceTargetSdk = enableAfterTargetSdk + 1;
+        } else if (enableSinceTargetSdk > 0) {
+            this.mEnableSinceTargetSdk = enableSinceTargetSdk;
+        } else {
+            this.mEnableSinceTargetSdk = -1;
+        }
         this.mDisabled = disabled;
         this.mLoggingOnly = loggingOnly;
         this.mDescription = description;
     }
 
+    public CompatibilityChangeInfo(CompatibilityChangeInfo other) {
+        this.mChangeId = other.mChangeId;
+        this.mName = other.mName;
+        this.mEnableSinceTargetSdk = other.mEnableSinceTargetSdk;
+        this.mDisabled = other.mDisabled;
+        this.mLoggingOnly = other.mLoggingOnly;
+        this.mDescription = other.mDescription;
+    }
+
     private CompatibilityChangeInfo(Parcel in) {
         mChangeId = in.readLong();
         mName = in.readString();
-        mEnableAfterTargetSdk = in.readInt();
+        mEnableSinceTargetSdk = in.readInt();
         mDisabled = in.readBoolean();
         mLoggingOnly = in.readBoolean();
         mDescription = in.readString();
@@ -87,7 +104,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mChangeId);
         dest.writeString(mName);
-        dest.writeInt(mEnableAfterTargetSdk);
+        dest.writeInt(mEnableSinceTargetSdk);
         dest.writeBoolean(mDisabled);
         dest.writeBoolean(mLoggingOnly);
         dest.writeString(mDescription);
@@ -100,8 +117,8 @@
         if (getName() != null) {
             sb.append("; name=").append(getName());
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+        if (getEnableSinceTargetSdk() != -1) {
+            sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk());
         }
         if (getDisabled()) {
             sb.append("; disabled");
@@ -123,7 +140,7 @@
         CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
         return this.mChangeId == that.mChangeId
                 && this.mName.equals(that.mName)
-                && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
+                && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk
                 && this.mDisabled == that.mDisabled
                 && this.mLoggingOnly == that.mLoggingOnly
                 && this.mDescription.equals(that.mDescription);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 6408def..cc266d6 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -164,7 +164,7 @@
     boolean clearOverride(long changeId, String packageName);
 
     /**
-     * Enable all compatibility changes which have enabledAfterTargetSdk ==
+     * Enable all compatibility changes which have enabledSinceTargetSdk ==
      * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
      * changes to take effect.
      *
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d23ea3c..a2af4d6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -132,6 +132,11 @@
      */
     public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
 
+    /**
+     * Whether to show app ops chip for location.
+     */
+    public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
+
     // Flags related to Assistant
 
     /**
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index af666d8..3682b7b 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -37,7 +37,6 @@
  * updating, and disappearing and reappearing on the SD card.
  */
 public abstract class PackageMonitor extends android.content.BroadcastReceiver {
-    static final String TAG = "PackageMonitor";
     static final IntentFilter sPackageFilt = new IntentFilter();
     static final IntentFilter sNonDataFilt = new IntentFilter();
     static final IntentFilter sExternalFilt = new IntentFilter();
@@ -49,9 +48,6 @@
         sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
         sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         sPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
-        sPackageFilt.addAction(Intent.ACTION_PACKAGE_STARTABLE);
-        sPackageFilt.addAction(Intent.ACTION_PACKAGE_UNSTARTABLE);
-        sPackageFilt.addAction(Intent.ACTION_PACKAGE_FULLY_LOADED);
         sPackageFilt.addDataScheme("package");
         sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
         sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
@@ -309,13 +305,6 @@
     public void onPackageDataCleared(String packageName, int uid) {
     }
 
-    /**
-     * Callback to indicate the package's state has changed.
-     * @param packageName Name of an installed package
-     * @param uid The UID the package runs under.
-     */
-    public void onPackageStateChanged(String packageName, int uid) {}
-
     public int getChangingUserId() {
         return mChangeUserId;
     }
@@ -463,21 +452,12 @@
             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             mSomePackagesChanged = true;
             onPackagesUnsuspended(pkgList);
-        } else if (Intent.ACTION_PACKAGE_STARTABLE.equals(action)
-                || Intent.ACTION_PACKAGE_UNSTARTABLE.equals(action)
-                || Intent.ACTION_PACKAGE_FULLY_LOADED.equals(action)) {
-            String pkg = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
-            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
-            mSomePackagesChanged = false;
-            if (pkg != null) {
-                onPackageStateChanged(pkg, uid);
-            }
         }
 
         if (mSomePackagesChanged) {
             onSomePackagesChanged();
         }
-
+        
         onFinishPackageChanges();
         mChangeUserId = UserHandle.USER_NULL;
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4c5f988..b986463 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -195,7 +195,7 @@
     static final int MSG_REPORT_POWER_CHANGE = 2;
     static final int MSG_REPORT_CHARGING = 3;
     static final int MSG_REPORT_RESET_STATS = 4;
-    static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
+    static final long DELAY_UPDATE_WAKELOCKS = 60 * 1000;
 
     private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
     private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L;
@@ -12574,11 +12574,11 @@
                 // This could happen if the isolated uid mapping was removed before that process
                 // was actually killed.
                 mCpuUidUserSysTimeReader.removeUid(uid);
-                Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
+                if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
                 mCpuUidUserSysTimeReader.removeUid(uid);
                 return;
             }
@@ -12683,11 +12683,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidFreqTimeReader.removeUid(uid);
-                Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
+                if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
                 mCpuUidFreqTimeReader.removeUid(uid);
                 return;
             }
@@ -12797,11 +12797,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidActiveTimeReader.removeUid(uid);
-                Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+                if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
                 mCpuUidActiveTimeReader.removeUid(uid);
                 return;
             }
@@ -12827,11 +12827,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidClusterTimeReader.removeUid(uid);
-                Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+                if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
                 mCpuUidClusterTimeReader.removeUid(uid);
                 return;
             }
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index dae649a..e80e545 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -31,6 +31,12 @@
  */
 public class Preconditions {
 
+    /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
     @UnsupportedAppUsage
     public static void checkArgument(boolean expression) {
         if (!expression) {
@@ -62,8 +68,9 @@
      * @param messageArgs arguments for {@code messageTemplate}
      * @throws IllegalArgumentException if {@code expression} is false
      */
-    public static void checkArgument(boolean expression,
-            final String messageTemplate,
+    public static void checkArgument(
+            final boolean expression,
+            final @NonNull String messageTemplate,
             final Object... messageArgs) {
         if (!expression) {
             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
@@ -114,7 +121,9 @@
      * @throws IllegalArgumentException if {@code string} is empty
      */
     public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
-            final T string, final String messageTemplate, final Object... messageArgs) {
+            final T string,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
         if (TextUtils.isEmpty(string)) {
             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
         }
@@ -160,18 +169,22 @@
     }
 
     /**
-     * Ensures the truth of an expression involving the state of the calling
-     * instance, but not involving any parameters to the calling method.
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
      *
-     * @param expression a boolean expression
-     * @param message exception message
-     * @throws IllegalStateException if {@code expression} is false
+     * @param messageTemplate a printf-style message template to use if the check fails; will
+     *     be converted to a string using {@link String#format(String, Object...)}
+     * @param messageArgs arguments for {@code messageTemplate}
+     * @throws NullPointerException if {@code reference} is null
      */
-    @UnsupportedAppUsage
-    public static void checkState(final boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalStateException(message);
+    public static @NonNull <T> T checkNotNull(
+            final T reference,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
+        if (reference == null) {
+            throw new NullPointerException(String.format(messageTemplate, messageArgs));
         }
+        return reference;
     }
 
     /**
@@ -187,6 +200,41 @@
     }
 
     /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    @UnsupportedAppUsage
+    public static void checkState(final boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @param messageTemplate a printf-style message template to use if the check fails; will
+     *     be converted to a string using {@link String#format(String, Object...)}
+     * @param messageArgs arguments for {@code messageTemplate}
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    public static void checkState(
+            final boolean expression,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
+        if (!expression) {
+            throw new IllegalStateException(String.format(messageTemplate, messageArgs));
+        }
+    }
+
+    /**
      * Ensures the truth of an expression involving whether the calling identity is authorized to
      * call the calling method.
      *
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index ed663cf..a761b4c 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -250,7 +250,7 @@
     // partition that is used to verify if an overlay package fulfills
     // the 'config_signature' policy by comparing their signatures:
     // if the overlay package is signed with the same certificate as
-    // the package declared in 'config-signature' tag, then the
+    // the package declared in 'overlay-config-signature' tag, then the
     // overlay package fulfills the 'config_signature' policy.
     private String mOverlayConfigSignaturePackage;
 
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a30c37b..b07c293 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -69,13 +69,19 @@
     queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
 }
 
+static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    queue->flushShadowQueue();
+}
+
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
         {"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
-        {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate}};
+        {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
+        {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue}};
 
 int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
     int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index e118038..32b8fa6 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -960,17 +960,8 @@
     return IPCThreadState::self()->clearCallingIdentity();
 }
 
-static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
+static void android_os_Binder_restoreCallingIdentity(jlong token)
 {
-    // XXX temporary validation check to debug crashes.
-    int uid = (int)(token>>32);
-    if (uid > 0 && uid < 999) {
-        // In Android currently there are no uids in this range.
-        char buf[128];
-        sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token);
-        jniThrowException(env, "java/lang/IllegalStateException", buf);
-        return;
-    }
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
@@ -1064,6 +1055,7 @@
     { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
     // @CriticalNative
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
+    // @CriticalNative
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     // @CriticalNative
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 42aab6a..f791cb1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -80,6 +80,7 @@
 #include <bionic/mte.h>
 #include <bionic/mte_kernel.h>
 #include <cutils/fs.h>
+#include <cutils/memory.h>
 #include <cutils/multiuser.h>
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
@@ -630,6 +631,13 @@
 
   // Set the jemalloc decay time to 1.
   mallopt(M_DECAY_TIME, 1);
+
+  // Avoid potentially expensive memory mitigations, mostly meant for system
+  // processes, in apps. These may cause app compat problems, use more memory,
+  // or reduce performance. While it would be nice to have them for apps,
+  // we will have to wait until they are proven out, have more efficient
+  // hardware, and/or apply them only to new applications.
+  process_disable_memory_mitigations();
 }
 
 static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index 013c65f..5268049 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -47,7 +47,7 @@
 static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
                                        const char* field_signature) {
     jfieldID res = env->GetFieldID(clazz, field_name, field_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find field %s with signature %s", field_name,
                         field_signature);
     return res;
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index d315ff2..0affcea 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,8 +48,8 @@
     optional string focused_app = 4;
     optional IdentifierProto input_method_window = 5;
     optional bool display_frozen = 6;
-    optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation"];
-    optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+    optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true];
+    optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation", deprecated=true];
     optional int32 focused_display_id = 9;
     optional bool hard_keyboard_available = 10;
 }
@@ -183,7 +183,7 @@
     repeated WindowTokenProto ime_windows = 8 [deprecated=true];
     optional int32 dpi = 9;
     optional .android.view.DisplayInfoProto display_info = 10;
-    optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true];
     optional ScreenRotationAnimationProto screen_rotation_animation = 12;
     optional DisplayFramesProto display_frames = 13;
     optional int32 surface_size = 14 [deprecated=true];
@@ -207,6 +207,8 @@
     optional WindowStateProto current_focus = 30;
     optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31;
     optional bool can_show_ime = 32;
+
+    optional DisplayRotationProto display_rotation = 33;
 }
 
 /* represents DisplayArea object */
@@ -242,6 +244,16 @@
     optional .android.graphics.RectProto current = 3;
 }
 
+message DisplayRotationProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 rotation = 1 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional bool frozen_to_user_rotation = 2;
+    optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional int32 fixed_to_user_rotation_mode = 4;
+    optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+}
+
 /* represents DockedStackDividerController */
 message DockedStackDividerControllerProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -529,4 +541,4 @@
     optional InsetsSourceProviderProto insets_source_provider = 1;
     optional WindowStateProto ime_target_from_ime = 2;
     optional bool is_ime_layout_drawn = 3;
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index d2c0ed4..e7fdde6 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -220,3 +220,43 @@
     // Error over IMS, retry on CS
     SMS_SEND_RESULT_ERROR_FALLBACK = 4;
 }
+
+// Data profile of the data call. From
+// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java
+enum DataProfileEnum {
+    DATA_PROFILE_INVALID = -1;
+    DATA_PROFILE_DEFAULT = 0;
+    DATA_PROFILE_TETHERED = 1;
+    DATA_PROFILE_IMS = 2;
+    DATA_PROFILE_FOTA = 3;
+    DATA_PROFILE_CBS = 4;
+    DATA_PROFILE_OEM_BASE = 1000;
+}
+
+// Reason of data call deactivation. From
+// frameworks/base/telephony/java/android/telephony/data/DataService.java#DeactivateDataReason
+enum DataDeactivateReasonEnum {
+    DEACTIVATE_REASON_UNKNOWN = 0;
+    DEACTIVATE_REASON_NORMAL = 1;
+    DEACTIVATE_REASON_RADIO_OFF = 2;
+    DEACTIVATE_REASON_HANDOVER = 3;
+}
+
+// IP type of the data call
+// see frameworks/base/telephony/java/android/telephony/data/ApnSetting.java#ProtocolType
+enum ApnProtocolEnum {
+    APN_PROTOCOL_IPV4 = 0;
+    APN_PROTOCOL_IPV6 = 1;
+    APN_PROTOCOL_IPV4V6 = 2;
+    APN_PROTOCOL_PPP = 3;
+}
+
+// Action taken to recover a data call that is stalled. From
+// frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+// #RecoveryAction
+enum DataStallRecoveryActionEnum {
+    RECOVERY_ACTION_GET_DATA_CALL_LIST = 0;
+    RECOVERY_ACTION_CLEANUP = 1;
+    RECOVERY_ACTION_REREGISTER = 2;
+    RECOVERY_ACTION_RADIO_RESTART = 3;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0a1b8e0..0195451 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2539,8 +2539,7 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to start activities from background
-         @hide -->
+    <!-- @SystemApi @hide Allows an application to start activities from background -->
     <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
         android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
 
diff --git a/core/res/res/layout/notification_material_media_transfer_action.xml b/core/res/res/layout/notification_material_media_transfer_action.xml
deleted file mode 100644
index 98d8f1e..0000000
--- a/core/res/res/layout/notification_material_media_transfer_action.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:visibility="gone"
-        android:padding="4dp"
-        android:layout_marginStart="10dp"
-        android:gravity="center"
-        android:background="@drawable/media_seamless_background">
-    <ImageView
-        android:layout_width="?attr/notificationHeaderIconSize"
-        android:layout_height="?attr/notificationHeaderIconSize"
-        android:src="@drawable/ic_media_seamless"
-        android:id="@+id/media_seamless_image" />
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
-        android:text="@string/ext_media_seamless_action"
-        android:id="@+id/media_seamless_text"
-        android:paddingEnd="2dp" />
-</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 9a1b592..88493c9 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -160,9 +160,5 @@
         android:visibility="gone"
         android:contentDescription="@string/notification_work_profile_content_description"
         />
-    <include
-        layout="@layout/notification_material_media_transfer_action"
-        android:id="@+id/media_seamless"
-    />
 </NotificationHeaderView>
 
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bd81519..20ebbd8 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0bbe232..d534758 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"አቋራጭ ይጠቀሙ"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ተቃራኒ ቀለም"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"የቀለም ማስተካከያ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለመጠቀም ለሦስት ሰከንዶች ሁለቱንም የድምፅ ቁልፎች ተጭነው ይያዙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8868fb6..86ed737 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1742,6 +1742,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 619666b..da99576 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শ্বৰ্টকাট ব্যৱহাৰ কৰক"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ৰং বিপৰীতকৰণ"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ৰং শুধৰণী"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ed708cc..bc3f146 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0b21545..f6dfe6e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 6a0ad98..f761cba 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колеру"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колеру"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index cdd0ec0..56ca128 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Използване на пряк път"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Инвертиране на цветовете"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Коригиране на цветовете"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Намаляване на ярките цветове"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"За да използвате <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натиснете двата бутона за силата на звука и ги задръжте за 3 секунди"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 779fd3e..ce0838c 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যবহার করতে ভলিউম কী বোতাম ৩ সেকেন্ডের জন্য চেপে ধরে রাখুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index fab6c91..95e6882 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Ispravka boja"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 96c6b6a..d115bde 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9a5f069..60fe94c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použít zkratku"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 480e87c..a8641d3 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Korriger farve"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ff8e6ad..68749c6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Verknüpfung verwenden"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Farbumkehr"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Farbkorrektur"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string>
@@ -1796,8 +1798,8 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
-    <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+    <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -2005,9 +2007,9 @@
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
-    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
-    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
-    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
+    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
+    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6f03f80..88edddf 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Χρήση συντόμευσης"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Αντιστροφή χρωμάτων"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Διόρθωση χρωμάτων"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index fac2f22..d439a7f 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduce bright colours"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 8497658..8052ced 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduce bright colours"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 12881e7..45e15c9 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduce bright colours"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index cb2eb7a..9527921 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduce bright colours"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5750e50..0bb1057 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎Use Shortcut‎‏‎‎‏‎"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Color Inversion‎‏‎‎‏‎"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎Color Correction‎‏‎‎‏‎"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎Reduce Bright Colors‎‏‎‎‏‎"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎Held volume keys. ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎Held volume keys. ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ turned off.‎‏‎‎‏‎"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎Press and hold both volume keys for three seconds to use ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8e7d1a33..85e5b96 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar acceso directo"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b9f0f58..39e04bd 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d825429..7883db3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 05d4050..4b4414b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Erabili lasterbidea"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Koloreen alderantzikatzea"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Koloreen zuzenketa"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 907fe5b..414addd 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -156,9 +156,9 @@
     <string name="httpErrorOk" msgid="6206751415788256357">"تأیید"</string>
     <string name="httpError" msgid="3406003584150566720">"خطایی در شبکه وجود داشت."</string>
     <string name="httpErrorLookup" msgid="3099834738227549349">"نشانی اینترنتی پیدا نشد."</string>
-    <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"‏طرح کلی احراز هویت سایت پشتیبانی نمی‌‎شود."</string>
+    <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"‏طرح کلی اصالت‌سنجی سایت پشتیبانی نمی‌‎شود."</string>
     <string name="httpErrorAuth" msgid="469553140922938968">"راستی‌آزمایی ناموفق بود."</string>
-    <string name="httpErrorProxyAuth" msgid="7229662162030113406">"احراز هویت از طریق سرور پروکسی انجام نشد."</string>
+    <string name="httpErrorProxyAuth" msgid="7229662162030113406">"اصالت‌سنجی از طریق سرور پروکسی انجام نشد."</string>
     <string name="httpErrorConnect" msgid="3295081579893205617">"اتصال به سرور انجام نشد."</string>
     <string name="httpErrorIO" msgid="3860318696166314490">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string>
     <string name="httpErrorTimeout" msgid="7446272815190334204">"زمان اتصال به سرور تمام شده است."</string>
@@ -529,11 +529,11 @@
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"درخواست پیچیدگی قفل صفحه"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"به برنامه اجازه می‌دهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین، یا هیچ‌کدام) را بیاموزد که نشان‌دهنده بازه ممکن طول و نوع قفل صفحه است. همچنین برنامه می‌تواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی به‌روزرسانی کنند، اما کاربران می‌توانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمی‌شود، بنابراین برنامه گذرواژه دقیق را نمی‌داند."</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"استفاده از سخت‌افزار بیومتریک"</string>
-    <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای اصالت‌سنجی استفاده کند"</string>
     <string name="permlab_manageFingerprint" msgid="7432667156322821178">"مدیریت سخت‌افزار اثر انگشت"</string>
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای اثر انگشت جهت استفاده، فعال کند."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"استفاده از سخت‌افزار اثر انگشت"</string>
-    <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان می‌دهد از سخت‌افزار اثر انگشت برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان می‌دهد از سخت‌افزار اثر انگشت برای اصالت‌سنجی استفاده کند"</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"تغییر مجموعه موسیقی شما"</string>
     <string name="permdesc_audioWrite" msgid="8057399517013412431">"به برنامه اجازه می‌دهد مجموعه موسیقی‌تان را تغییر دهد."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"تغییر مجموعه ویدیوی شما"</string>
@@ -544,11 +544,11 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه می‌دهد مکان‌ها را از مجموعه رسانه‌تان بخواند."</string>
     <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شما هستید"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سخت‌افزار زیست‌سنجی دردسترس نیست"</string>
-    <string name="biometric_error_user_canceled" msgid="6732303949695293730">"احراز هویت لغو شد"</string>
+    <string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالت‌سنجی لغو شد"</string>
     <string name="biometric_not_recognized" msgid="5106687642694635888">"شناسایی نشد"</string>
-    <string name="biometric_error_canceled" msgid="8266582404844179778">"احراز هویت لغو شد"</string>
+    <string name="biometric_error_canceled" msgid="8266582404844179778">"اصالت‌سنجی لغو شد"</string>
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"پین، الگو یا گذرواژه‌ای تنظیم نشده است"</string>
-    <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام احراز هویت"</string>
+    <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام اصالت‌سنجی"</string>
     <string name="fingerprint_acquired_partial" msgid="8532380671091299342">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
@@ -556,9 +556,9 @@
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت احراز هویت شد"</string>
-    <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره احراز هویت شد"</string>
-    <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره احراز هویت شد، لطفاً تأیید را فشار دهید"</string>
+    <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالت‌سنجی شد"</string>
+    <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالت‌سنجی شد"</string>
+    <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
     <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سخت‌افزار اثرانگشت در دسترس نیست."</string>
     <string name="fingerprint_error_no_space" msgid="6126456006769817485">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
     <string name="fingerprint_error_timeout" msgid="2946635815726054226">"درنگ ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
@@ -577,7 +577,7 @@
     <string name="permlab_manageFace" msgid="4569549381889283282">"مدیریت سخت‌افزار «بازگشایی با چهره»"</string>
     <string name="permdesc_manageFace" msgid="6204569688492710471">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
     <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استفاده از سخت‌افزار «بازگشایی با چهره»"</string>
-    <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان می‌دهد از سخت‌افزار «بازگشایی با چهره» برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان می‌دهد از سخت‌افزار «بازگشایی با چهره» برای اصالت‌سنجی استفاده کند"</string>
     <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"بازگشایی با چهره"</string>
     <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ثبت مجدد چهره"</string>
     <string name="face_recalibrate_notification_content" msgid="892757485125249962">"برای بهبود تشخیص، لطفاً چهره‌تان را دوباره ثبت کنید"</string>
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میان‌بر"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e7bcf48..dc40a02 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index aad5749..582c966 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d2bdbbb..167249f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 79380d5..e78a0c8 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atallo"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de cor"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de cor"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index bf4d6c0..e38096c 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>નો ઉપયોગ કરવા માટે બન્ને વૉલ્યૂમ કીને ત્રણ સેકન્ડ સુધી દબાવી રાખો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b867e9d..59c2b2d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट का उपयोग करें"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"रंग बदलने की सुविधा"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"रंग में सुधार करने की सुविधा"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> इस्तेमाल करने के लिए आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index dd2bdbe..c3edbf5 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Upotrijebi prečac"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boje"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9d1c2f4..47cf41b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4e2bbe2..1e1d1f9 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"«<xliff:g id="SERVICE_NAME">%1$s</xliff:g>» ծառայությունն օգտագործելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակները"</string>
@@ -1912,7 +1914,7 @@
     <string name="app_info" msgid="6113278084877079851">"Հավելվածի մասին"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Ցուցադրական օգտատերը գործարկվում է…"</string>
-    <string name="demo_restarting_message" msgid="1160053183701746766">"Սարաքը վերակայվում է…"</string>
+    <string name="demo_restarting_message" msgid="1160053183701746766">"Սարքը վերակայվում է…"</string>
     <string name="suspended_widget_accessibility" msgid="6331451091851326101">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="5731633152336490471">"Կոնֆերանս զանգ"</string>
     <string name="tooltip_popup_title" msgid="7863719020269945722">"Հուշակ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 202664f..8a09de4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversi Warna"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Koreksi Warna"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index efb23d4..2c7ee6b 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ec446d7..e32609a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 874d577..2edfa4d 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"השתמש בקיצור הדרך"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"יש ללחוץ לחיצה ארוכה על שני לחצני עוצמת הקול למשך שלוש שניות כדי להשתמש בשירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 32a4ded..c9fc1c2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 349bd16..060d727 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"მალსახმობის გამოყენება"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ფერთა ინვერსია"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ფერთა კორექცია"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"კაშკაშა ფერების შემცირება"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> რომ გამოიყენოთ, დააჭირეთ ხმის ორივე ღილაკზე 3 წამის განმავლობაში"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 4d4eef0..d80572f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3ebd736..2fd7257 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់​ផ្លូវកាត់"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាស​ពណ៌"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ការ​កែ​ពណ៌"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ចុចគ្រាប់ចុច​កម្រិត​សំឡេងទាំងពីរ​ឱ្យជាប់រយៈពេលបីវិនាទី ដើម្បីប្រើ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0cf6f49..20b2ccf 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್‌ಕಟ್ ಬಳಸಿ"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್‌ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3dfdf3d..79569e2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 서비스를 사용하려면 두 볼륨 키를 3초 동안 길게 누르세요"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 012acf5..4fee44a 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 315ee32..457b74e 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ໃຊ້ປຸ່ມລັດ"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ການປີ້ນສີ"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ການແກ້ໄຂຄ່າສີ"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"ຫຼຸດສີສະຫວ່າງ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ກົດປຸ່ມສຽງທັງສອງພ້ອມກັນຄ້າງໄວ້ສາມວິນາທີເພື່ອໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1d6b2c7..cedde11 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ba790a0..e34bb95 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c882cb1..c061283 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од три секунди за да користите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ffe073c..18dbf0e 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 2183d010..a7a08ce 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г ашиглахын тулд дууны түвшнийг ихэсгэх, багасгах түлхүүрийг 3 секундийн турш зэрэг дарна уу"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 3d2fbcb..b9ef667 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -197,7 +197,7 @@
     <string name="country_detector" msgid="7023275114706088854">"कंट्री डिटेक्टर"</string>
     <string name="location_service" msgid="2439187616018455546">"स्थान सेवा"</string>
     <string name="gnss_service" msgid="8907781262179951385">"GNSS सेवा"</string>
-    <string name="sensor_notification_service" msgid="7474531979178682676">"सेंसर सूचना सेवा"</string>
+    <string name="sensor_notification_service" msgid="7474531979178682676">"सेन्सर सूचना सेवा"</string>
     <string name="twilight_service" msgid="8964898045693187224">"ट्वायलाइट सेवा"</string>
     <string name="factory_reset_warning" msgid="6858705527798047809">"तुमचे डिव्हाइस मिटविले जाईल"</string>
     <string name="factory_reset_message" msgid="2657049595153992213">"प्रशासक अ‍ॅप वापरता येणार नाही. तुमचे डिव्हाइस आता साफ केले जाईल.\n\nतुम्हाला कुठलेही प्रश्न असल्यास, तुमच्या संस्थेच्या प्रशासकाशी संपर्क साधा."</string>
@@ -316,7 +316,7 @@
     <string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"फोन कॉल आणि व्यवस्थापित"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"शरीर सेन्सर"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्‍या महत्त्वाच्या मापनांविषयी सेंसर डेटा अ‍ॅक्सेस करा"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्‍या महत्त्वाच्या मापनांविषयी सेन्सर डेटा अ‍ॅक्सेस करा"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडोमधील आशय पुन्हा मिळवा"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तुम्ही वापरत असलेल्‍या विंडोमधील आशय तपासा."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"स्पर्श करून अन्वेषण सुरू करा"</string>
@@ -388,7 +388,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"अ‍ॅप संचयन स्थान मोजा"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"अ‍ॅप ला त्याचा कोड, डेटा आणि कॅशे    आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग्ज सुधारित करा"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string>
@@ -411,7 +411,7 @@
     <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी ॲपला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
-    <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अ‍ॅक्सेस करा"</string>
+    <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेन्सर (हृदय गती मॉनिटरसारखे) अ‍ॅक्सेस करा"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्‍या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"हा अ‍ॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
@@ -565,7 +565,7 @@
     <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
     <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
     <string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
-    <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेंसर बंद आहे."</string>
+    <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेन्सर बंद आहे."</string>
     <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string>
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
@@ -657,7 +657,7 @@
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"स्‍वप्न सेवेवर प्रतिबद्ध करा"</string>
     <string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्‍डरला स्‍वप्नसेवेच्या शीर्ष-स्‍तराच्या इंटरफेसशी प्रतिबद्ध करण्‍यास अनुमती देते. सामान्‍य अ‍ॅप्‍सकरिता कधीही आवश्‍यक नसते."</string>
     <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहकाद्वारे-प्रदान केलेल्‍या कॉन्‍फिगरेशन अ‍ॅपची विनंती करा"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉन्फिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉंफिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"नेटवर्क स्‍थितींवरील निरीक्षणांसाठी ऐका"</string>
     <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"अनु्प्रयोगाला नेटवर्क स्‍थितींवरील निरीक्षणे ऐकण्‍यासाठी अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
@@ -673,7 +673,7 @@
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवांवर प्रतिबद्ध करा"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"वाहक सेवांवर प्रतिबद्ध करण्यासाठी होल्डरला अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यकता नसावी."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"व्यत्यय आणू नका अ‍ॅक्सेस करा"</string>
-    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉन्फिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
+    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string>
@@ -752,7 +752,7 @@
   </string-array>
     <string name="phoneTypeCustom" msgid="5120365721260686814">"कस्टम"</string>
     <string name="phoneTypeHome" msgid="3880132427643623588">"घर"</string>
-    <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाईल"</string>
+    <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाइल"</string>
     <string name="phoneTypeWork" msgid="6604967163358864607">"कार्य"</string>
     <string name="phoneTypeFaxWork" msgid="6757519896109439123">"कार्य फॅक्स"</string>
     <string name="phoneTypeFaxHome" msgid="6678559953115904345">"निवास फॅक्स"</string>
@@ -767,7 +767,7 @@
     <string name="phoneTypeRadio" msgid="2637819130239264771">"रेडिओ"</string>
     <string name="phoneTypeTelex" msgid="2558783611711876562">"टेलेक्स"</string>
     <string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
-    <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाईल"</string>
+    <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाइल"</string>
     <string name="phoneTypeWorkPager" msgid="3748332310638505234">"कार्य पेजर"</string>
     <string name="phoneTypeAssistant" msgid="757550783842231039">"असिस्टंट"</string>
     <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
@@ -779,7 +779,7 @@
     <string name="emailTypeHome" msgid="1597116303154775999">"घर"</string>
     <string name="emailTypeWork" msgid="2020095414401882111">"कार्य"</string>
     <string name="emailTypeOther" msgid="5131130857030897465">"अन्य"</string>
-    <string name="emailTypeMobile" msgid="787155077375364230">"मोबाईल"</string>
+    <string name="emailTypeMobile" msgid="787155077375364230">"मोबाइल"</string>
     <string name="postalTypeCustom" msgid="5645590470242939129">"कस्टम"</string>
     <string name="postalTypeHome" msgid="7562272480949727912">"घर"</string>
     <string name="postalTypeWork" msgid="8553425424652012826">"कार्य"</string>
@@ -1273,8 +1273,8 @@
     <string name="sms_control_yes" msgid="4858845109269524622">"अनुमती द्या"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"नकार द्या"</string>
     <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; हा &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;वर एक मेसेज पाठवू इच्छितो."</string>
-    <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाईल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string>
-    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाईल खात्यावर शुल्क आकारले जाऊ शकते."</b></string>
+    <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाइल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string>
+    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाइल खात्यावर शुल्क आकारले जाऊ शकते."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"पाठवा"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"रद्द करा"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"माझी वड लक्षात ठेवा"</string>
@@ -1282,10 +1282,10 @@
     <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"नेहमी अनुमती द्या"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"कधीही अनुमती देऊ नका"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"सिम कार्ड काढले"</string>
-    <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाईल नेटवर्क अनुपलब्ध असेल."</string>
+    <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाइल नेटवर्क अनुपलब्ध असेल."</string>
     <string name="sim_done_button" msgid="6464250841528410598">"पूर्ण झाले"</string>
     <string name="sim_added_title" msgid="7930779986759414595">"सिम कार्ड जोडले"</string>
-    <string name="sim_added_message" msgid="6602906609509958680">"मोबाईल नेटवर्कवर अ‍ॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string>
+    <string name="sim_added_message" msgid="6602906609509958680">"मोबाइल नेटवर्कवर अ‍ॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string>
     <string name="sim_restart_button" msgid="8481803851341190038">"रीस्टार्ट"</string>
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"मोबाइल सेवा अ‍ॅक्टिव्हेट करा"</string>
     <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"तुमचे नवीन सिम अ‍ॅक्टिव्हेट करण्यासाठी वाहकाचे अ‍ॅप डाउनलोड करा"</string>
@@ -1336,7 +1336,7 @@
     <string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string>
     <string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
     <string name="hardware" msgid="1800597768237606953">"व्हर्च्युअल कीबोर्ड दर्शवा"</string>
-    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string>
+    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉंफिगर करा"</string>
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string>
     <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
@@ -1914,7 +1916,7 @@
     <string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ करत आहे..."</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"डिव्हाइस रीसेट करत आहे..."</string>
     <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
-    <string name="conference_call" msgid="5731633152336490471">"कॉन्फरन्स कॉल"</string>
+    <string name="conference_call" msgid="5731633152336490471">"कॉंफरन्स कॉल"</string>
     <string name="tooltip_popup_title" msgid="7863719020269945722">"टूलटिप"</string>
     <string name="app_category_game" msgid="4534216074910244790">"गेम"</string>
     <string name="app_category_audio" msgid="8296029904794676222">"संगीत आणि ऑडिओ"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5c35ba1..d0b9f3a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Penyongsangan Warna"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Pembetulan Warna"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 38f1aa8..0729f53 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"အရောင်ပြင်ဆင်ခြင်း"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5b0a4ad..1839426 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Fargeinvertering"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Fargekorrigering"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 092686b..b75a05d 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -78,18 +78,18 @@
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपतकालीन कल सेवा उपलब्ध छैन"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्‌कालीन कल सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"कुनै पनि भ्वाइस वा आपातकालीन कल सेवा उपलब्ध छैन"</string>
     <string name="RestrictedStateContent" msgid="7693575344608618926">"तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
     <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> का लागि तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्कमाथि पहुँच राख्न सकिएन"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"रुचाइएको नेटवर्क परिवर्तन गरी हेर्नुहोस्‌। परिवर्तन गर्न ट्याप गर्नुहोस्‌।"</string>
-    <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपतकालीन कल सेवा अनुपलब्ध छ"</string>
-    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपतकालीन कल गर्न सकिँदैन"</string>
+    <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपत्‌कालीन कल सेवा अनुपलब्ध छ"</string>
+    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपत्‌कालीन कल गर्न सकिँदैन"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"अलर्टहरू"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"कल फर्वार्ड गर्ने सेवा"</string>
-    <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपतकालीन कलब्याक मोड"</string>
+    <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपत्‌कालीन कलब्याक मोड"</string>
     <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"मोबाइल डेटाको स्थिति"</string>
     <string name="notification_channel_sms" msgid="1243384981025535724">"SMS सन्देशहरू"</string>
     <string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल सन्देशहरू"</string>
@@ -241,7 +241,7 @@
     <string name="global_action_power_off" msgid="4404936470711393203">"बन्द गर्नुहोस्"</string>
     <string name="global_action_power_options" msgid="1185286119330160073">"पावर"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"पुनः सुरु गर्नुहोस्"</string>
-    <string name="global_action_emergency" msgid="1387617624177105088">"आपतकालीन"</string>
+    <string name="global_action_emergency" msgid="1387617624177105088">"आपत्‌कालीन"</string>
     <string name="global_action_bug_report" msgid="5127867163044170003">"बग रिपोर्ट"</string>
     <string name="global_action_logout" msgid="6093581310002476511">"सत्रको अन्त्य गर्नुहोस्"</string>
     <string name="global_action_screenshot" msgid="2610053466156478564">"स्क्रिनसट"</string>
@@ -345,24 +345,24 @@
     <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"एपलाई अन्य नम्बरमा कल पुर्ननिर्देश वा समग्र कल परित्याग विकल्प सहित बहिर्गमन कल समयमा डायल गर्दाको नम्बर हेर्न अनुमति दिन्छ।"</string>
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"फोन कलहरूको जवाफ दिनुहोस्"</string>
     <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"एपलाई आगमन फोन कलको जवाफ दिन अनुमति दिन्छ।"</string>
-    <string name="permlab_receiveSms" msgid="505961632050451881">"पाठ सन्देशहरू (SMS) प्राप्त गर्नुहोस्"</string>
+    <string name="permlab_receiveSms" msgid="505961632050451881">"टेक्स्ट म्यासेजहरू (SMS) प्राप्त गर्नुहोस्"</string>
     <string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
     <string name="permlab_receiveMms" msgid="4000650116674380275">"पाठ सन्देश (MMS) प्राप्त गर्नुहोस्"</string>
     <string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्‌कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्‌कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपतकालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपतकालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
+    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्‌कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्‌कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"एपलाई अहिलेको समीकरण गरिएका सूचकहरू बारे विवरणहरू लिने अनुमति दिन्छ।"</string>
     <string name="permlab_sendSms" msgid="7757368721742014252">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
     <string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
-    <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्"</string>
+    <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका टेक्स्ट म्यासेजहरू (SMS वा MMS) पढ्नुहोस्"</string>
     <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
     <string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
-    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्"</string>
-    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
+    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string>
+    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
     <string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
@@ -451,7 +451,7 @@
     <string name="permdesc_vibrate" msgid="8733343234582083721">"एपलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string>
     <string name="permdesc_vibrator_state" msgid="7050024956594170724">"यो एपलाई कम्पनको स्थितिमाथि पहुँच राख्न दिनुहोस्।"</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सीधै कल गर्नुहोस्"</string>
-    <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
+    <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपत्‌कालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS कल सेवा पहुँच गर्नुहोस्"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string>
@@ -830,13 +830,13 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"अनलक गर्न PIN कोड टाइप गर्नुहोस्"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"गलत PIN कोड।"</string>
     <string name="keyguard_label_text" msgid="3841953694564168384">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपतकालीन नम्बर"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपत्‌कालीन नम्बर"</string>
     <string name="lockscreen_carrier_default" msgid="6192313772955399160">"कुनै सेवा छैन"</string>
     <string name="lockscreen_screen_locked" msgid="7364905540516041817">"स्क्रिन लक गरिएको।"</string>
-    <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
+    <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपत्‌कालीन कल गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
-    <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपतकालीन कल"</string>
+    <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपत्‌कालीन कल"</string>
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
@@ -858,7 +858,7 @@
     <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"रोक्नुहोस्"</string>
     <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"दोहोर्याउनुहोस्"</string>
     <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"फास्ट फर्वार्ड"</string>
-    <string name="emergency_calls_only" msgid="3057351206678279851">"आपतकालीन कलहरू मात्र"</string>
+    <string name="emergency_calls_only" msgid="3057351206678279851">"आपत्‌कालीन कलहरू मात्र"</string>
     <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"नेटवर्क लक छ"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM कार्ड PUK-लक गरिएको छ।"</string>
     <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"प्रयोगकर्ता निर्देशक वा ग्राहक सेवा सम्पर्क हर्नुहोस्।"</string>
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string>
@@ -1966,7 +1968,7 @@
     <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
     <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"तटीय क्षेत्र र नदीछेउका ठाउँहरू छाडी उच्च सतहमा अवस्थित कुनै अझ सुरक्षित ठाउँमा जानुहोस्।"</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
-    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपतकालीन सन्देशहरूको परीक्षण"</string>
+    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपत्‌कालीन सन्देशहरूको परीक्षण"</string>
     <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"जवाफ दिनु…"</string>
     <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
     <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM मार्फत भ्वाइस कल गर्न मिल्दैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 17e988e4..d460f40 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurcorrectie"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is ingeschakeld."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0f10cde..129f4d3 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍‍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍‍ କୀ ଦବାଇ ଧରି ରଖନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 11bfd78..ccd2039 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"ਰੰਗ ਪਲਟਨਾ"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"ਰੰਗ ਸੁਧਾਈ"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d626513..a55ca4c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Użyj skrótu"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Odwrócenie kolorów"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcja kolorów"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 450fddb..66a2956 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduzir cores brilhantes"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6862001..c40eca0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atalho"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção da cor"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduzir cores brilhantes"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 450fddb..66a2956 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1654,6 +1654,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="6722364385073799185">"Reduzir cores brilhantes"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f9e608f..2914094 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index be24770..e241bb7 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c23e475..bb2da61b 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාත්මකයි."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාවිරහිතයි."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> භාවිත කිරීමට හඬ පරිමා යතුරු දෙකම තත්පර තුනකට ඔබාගෙන සිටින්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 980a945..02f25f5 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 573a082..7fd0243 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f6f3594..13ad398 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 366d29b..42dc695 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1676,6 +1676,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 988fb93..66ba07f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fd390d8..5c0ab95 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index c764a20..23b7211 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"கலர் இன்வெர்ஷன்"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"வண்ணத் திருத்தம்"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐப் பயன்படுத்த 3 விநாடிகளுக்கு இரண்டு ஒலியளவு பட்டன்களையும் அழுத்திப் பிடிக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 23292a2..9b03dcb 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d5d21c6..fc33060 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ใช้ทางลัด"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"การกลับสี"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"การแก้สี"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f53851d..bc67249 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5aa336e..c870742 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index da41508..3af955d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1698,6 +1698,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 94fad25..095be18 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -274,8 +274,8 @@
     <string name="notification_channel_security" msgid="8516754650348238057">"سیکیورٹی"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"کار وضع"</string>
     <string name="notification_channel_account" msgid="6436294521740148173">"اکاؤنٹ اسٹیٹس"</string>
-    <string name="notification_channel_developer" msgid="1691059964407549150">"ڈیولپر کے پیغامات"</string>
-    <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈیولپر پیغامات"</string>
+    <string name="notification_channel_developer" msgid="1691059964407549150">"ڈویلپر کے پیغامات"</string>
+    <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈویلپر پیغامات"</string>
     <string name="notification_channel_updates" msgid="7907863984825495278">"اپ ڈیٹس"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"نیٹ ورک اسٹیٹس"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"نیٹ ورک الرٹس"</string>
@@ -1218,7 +1218,7 @@
     <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> ہیپ ڈمپ تیار ہے"</string>
     <string name="dump_heap_notification_detail" msgid="8431586843001054050">"ہیپ ڈمپ جمع ہو گیا ہے۔ اشتراک کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="dump_heap_title" msgid="4367128917229233901">"ہیپ ڈمپ کا اشتراک کریں؟"</string>
-    <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈیولپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string>
+    <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈویلپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string>
     <string name="dump_heap_system_text" msgid="6805155514925350849">"<xliff:g id="PROC">%1$s</xliff:g> پروسیس نے اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے بڑھا لی ہے۔ آپ کے اشتراک کرنے کے لیے ہیپ ڈمپ دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string>
     <string name="dump_heap_ready_text" msgid="5849618132123045516">"<xliff:g id="PROC">%1$s</xliff:g> کے پروسیس کا ہیپ ڈمپ آپ کے اشتراک کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں ممکنہ طور پر حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string>
     <string name="sendText" msgid="493003724401350724">"متن کیلئے ایک کارروائی منتخب کریں"</string>
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> کا استعمال کرنے کے لیے 3 سیکنڈ تک والیوم کی دونوں کلیدوں کو چھوئیں اور دبائے رکھیں"</string>
@@ -1896,7 +1898,7 @@
     <string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
-    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈیولپر سے رابطہ کریں۔"</string>
+    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈویلپر سے رابطہ کریں۔"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"‏دیکھنے کیلئے SMS ایپ کھولیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 61d29dc..d1d5115 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tezkor ishga tushirishdan foydalanish"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Ranglarni akslantirish"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Rangni tuzatish"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index fd7205d..6be3ac8 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 16775d5..62b8937 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快捷方式"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"颜色反转"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bd13574..54f2a87 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快速鍵"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0637742..e71f794 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用捷徑"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同時按住調低及調高音量鍵三秒即可使用「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ea97a6e..cc4a3d0 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1654,6 +1654,8 @@
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string>
+    <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 25c64a9..e3ddbd8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3200,6 +3200,15 @@
          then the system will set the same minimal height on all other activities in the task. It
          will also ignore any other minimal height attributes of non-root activities. -->
         <attr name="minHeight" />
+
+        <!-- Window layout affinity of this activity. Activities with the same window layout
+          affinity will share the same layout record. If an activity is launched in freeform window,
+          the activity will be launched to the latest position and size where any task, if the root
+          activity of that task shares the same window layout affinity with the activity being
+          launched. Window layout affinity is shared only among activities with the same UID.
+
+          <p>By default activity doesn't share any affinity with other activities. -->
+        <attr name="windowLayoutAffinity" format="string" />
     </declare-styleable>
 
     <!-- <code>restrict-update</code> tag restricts system apps from being updated unless the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c4d0963..b4b634a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1686,6 +1686,21 @@
         -->
     </string-array>
 
+    <!-- Optional IPsec algorithms enabled by this device, defaulting to empty. OEMs can override
+         it by providing a list of algorithm names in an overlay config.xml file.
+
+         As Android releases new versions, more algorithms are becoming mandatory. Mandatory
+         algorithms will be automatically enabled on the device. Optional algorithms need
+         to be explicitly declared in this resource to be enabled.
+             * SDK level 28 makes the following algorithms mandatory : "cbc(aes)", "hmac(md5)",
+               "hmac(sha1)", "hmac(sha256)", "hmac(sha384)", "hmac(sha512)", "rfc4106(gcm(aes))"
+             * SDK level 30 makes the following algorithms mandatory : "rfc3686(ctr(aes))",
+               "xcbc(aes)", "rfc7539esp(chacha20,poly1305)"
+     -->
+    <string-array name="config_optionalIpSecAlgorithms" translatable="false">
+        <!-- Add algorithm here -->
+    </string-array>
+
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
     <bool name="config_bluetooth_sco_off_call">true</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fe17eca..45bdff9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3046,6 +3046,7 @@
   <public-group type="attr" first-id="0x01010617">
     <public name="rollbackDataPolicy" />
     <public name="allowClickWhenDisabled" />
+    <public name="windowLayoutAffinity" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
index 40db9df..e17d214 100644
--- a/core/res/res/values/required_apps_managed_device.xml
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -21,6 +21,7 @@
             Takes precedence over the disallowed apps lists. -->
     <string-array translatable="false" name="required_apps_managed_device">
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
         <item>com.android.stk</item>  <!-- Required by com.android.phone by certain carriers -->
diff --git a/core/res/res/values/required_apps_managed_profile.xml b/core/res/res/values/required_apps_managed_profile.xml
index c6b37e8..6ed385a 100644
--- a/core/res/res/values/required_apps_managed_profile.xml
+++ b/core/res/res/values/required_apps_managed_profile.xml
@@ -22,6 +22,7 @@
     <string-array translatable="false" name="required_apps_managed_profile">
         <item>com.android.contacts</item>
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.providers.downloads</item>
         <item>com.android.providers.downloads.ui</item>
         <item>com.android.documentsui</item>
diff --git a/core/res/res/values/required_apps_managed_user.xml b/core/res/res/values/required_apps_managed_user.xml
index 8800e535..a6fc573 100644
--- a/core/res/res/values/required_apps_managed_user.xml
+++ b/core/res/res/values/required_apps_managed_user.xml
@@ -21,6 +21,7 @@
             Takes precedence over the disallowed apps lists. -->
     <string-array translatable="false" name="required_apps_managed_user">
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
         <item>com.android.stk</item>  <!-- Required by com.android.phone by certain carriers -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9858f5e..caa3dff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -200,9 +200,6 @@
   <java-symbol type="id" name="action2" />
   <java-symbol type="id" name="action3" />
   <java-symbol type="id" name="action4" />
-  <java-symbol type="id" name="media_seamless" />
-  <java-symbol type="id" name="media_seamless_image" />
-  <java-symbol type="id" name="media_seamless_text" />
   <java-symbol type="id" name="notification_media_seekbar_container" />
   <java-symbol type="id" name="notification_media_content" />
   <java-symbol type="id" name="notification_media_progress" />
@@ -3196,6 +3193,9 @@
   <!-- Network Recommendation -->
   <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
 
+  <!-- Optional IPsec algorithms -->
+  <java-symbol type="array" name="config_optionalIpSecAlgorithms" />
+
   <!-- Whether allow 3rd party apps on internal storage. -->
   <java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" />
 
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c6ef094..e2b975f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -54,7 +54,6 @@
         "android.test.mock",
         "framework-atb-backward-compatibility",
         "framework",
-        "icing-java-proto-lite",
         "ext",
         "framework-res",
     ],
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 38dce15..bb826de 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -190,6 +190,17 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.CustomInputConnectionEditTextActivity"
+                  android:label="CustomInputConnectionEditTextActivity"
+                  android:screenOrientation="portrait"
+                  android:exported="true"
+                  android:theme="@android:style/Theme.Material.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.TextViewActivity"
                 android:label="TextViewActivity"
                 android:screenOrientation="portrait"
diff --git a/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml
new file mode 100644
index 0000000..c4db8be
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <android.widget.CustomInputConnectionEditText
+        android:id="@+id/edittext1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <android.widget.CustomInputConnectionEditText
+        android:id="@+id/edittext2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
deleted file mode 100644
index ac0f44b..0000000
--- a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 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.app.appsearch;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.SearchResultProto;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-@SmallTest
-public class SearchResultsTest {
-
-    @Test
-    public void testSearchResultsEqual() {
-        final String uri = "testUri";
-        final String schemaType = "testSchema";
-        SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(DocumentProto.newBuilder()
-                        .setUri(uri)
-                        .setSchema(schemaType)
-                        .build())
-                .build();
-        SearchResultProto searchResults1 = SearchResultProto.newBuilder()
-                .addResults(result1)
-                .build();
-        SearchResults res1 = new SearchResults(searchResults1);
-        SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(DocumentProto.newBuilder()
-                        .setUri(uri)
-                        .setSchema(schemaType)
-                        .build())
-                .build();
-        SearchResultProto searchResults2 = SearchResultProto.newBuilder()
-                .addResults(result2)
-                .build();
-        SearchResults res2 = new SearchResults(searchResults2);
-        assertThat(res1.toString()).isEqualTo(res2.toString());
-    }
-
-    @Test
-    public void buildSearchSpecWithoutTermMatchType() {
-        assertThrows(RuntimeException.class, () -> new SearchSpec.Builder()
-                .setSchemaTypes("testSchemaType")
-                .build());
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
similarity index 94%
rename from core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 6aa16cc..ac2d4b5 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -18,11 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 
-@SmallTest
 public class AppSearchEmailTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
similarity index 69%
rename from core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index 54a281f2..1f2c12b 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -18,36 +18,25 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.assertThrows;
-
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.PropertyProto;
-import android.app.appsearch.protobuf.ByteString;
-
-import androidx.test.filters.SmallTest;
-
+import static org.testng.Assert.expectThrows;
 
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-@SmallTest
-public class AppSearchDocumentTest {
+public class GenericDocumentTest {
     private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
-    private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
-    private static final AppSearchDocument sDocumentProperties1 = new AppSearchDocument
+    private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
+    private static final GenericDocument sDocumentProperties1 = new GenericDocument
             .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+            .setCreationTimestampMillis(12345L)
             .build();
-    private static final AppSearchDocument sDocumentProperties2 = new AppSearchDocument
+    private static final GenericDocument sDocumentProperties2 = new GenericDocument
             .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+            .setCreationTimestampMillis(6789L)
             .build();
 
     @Test
     public void testDocumentEquals_Identical() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setTtlMillis(1L)
                 .setProperty("longKey1", 1L, 2L, 3L)
@@ -57,7 +46,7 @@
                 .setProperty("byteKey1", sByteArray1, sByteArray2)
                 .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
                 .build();
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setTtlMillis(1L)
                 .setProperty("longKey1", 1L, 2L, 3L)
@@ -73,7 +62,7 @@
 
     @Test
     public void testDocumentEquals_DifferentOrder() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .setProperty("byteKey1", sByteArray1, sByteArray2)
@@ -84,7 +73,7 @@
                 .build();
 
         // Create second document with same parameter but different order.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, false, true)
                 .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
@@ -99,13 +88,13 @@
 
     @Test
     public void testDocumentEquals_Failure() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .build();
 
         // Create second document with same order but different value.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 4L) // Different
                 .build();
@@ -115,13 +104,13 @@
 
     @Test
     public void testDocumentEquals_Failure_RepeatedFieldOrder() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, false, true)
                 .build();
 
         // Create second document with same order but different value.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, true, false) // Different
                 .build();
@@ -131,11 +120,10 @@
 
     @Test
     public void testDocumentGetSingleValue() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setScore(1)
                 .setTtlMillis(1L)
-                .setScore(1)
                 .setProperty("longKey1", 1L)
                 .setProperty("doubleKey1", 1.0)
                 .setProperty("booleanKey1", true)
@@ -159,7 +147,7 @@
 
     @Test
     public void testDocumentGetArrayValues() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .setProperty("doubleKey1", 1.0, 2.0, 3.0)
@@ -185,8 +173,51 @@
     }
 
     @Test
+    public void testDocument_ToString() throws Exception {
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setProperty("longKey1", 1L, 2L, 3L)
+                .setProperty("doubleKey1", 1.0, 2.0, 3.0)
+                .setProperty("booleanKey1", true, false, true)
+                .setProperty("stringKey1", "String1", "String2", "String3")
+                .setProperty("byteKey1", sByteArray1, sByteArray2)
+                .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
+                .build();
+        String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } "
+                + "{ key: 'namespace' value:  } "
+                + "{ key: 'properties' value: "
+                +       "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
+                +       "{ key: 'byteKey1' value: "
+                +             "{ key: 'byteArray' value: [ '1' '2' '3' ] } "
+                +             "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] }  } "
+                +       "{ key: 'documentKey1' value: [ '"
+                +             "{ key: 'creationTimestampMillis' value: 12345 } "
+                +             "{ key: 'namespace' value:  } "
+                +             "{ key: 'properties' value:  } "
+                +             "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
+                +             "{ key: 'score' value: 0 } "
+                +             "{ key: 'ttlMillis' value: 0 } "
+                +             "{ key: 'uri' value: sDocumentProperties1 } ' '"
+                +             "{ key: 'creationTimestampMillis' value: 6789 } "
+                +             "{ key: 'namespace' value:  } "
+                +             "{ key: 'properties' value:  } "
+                +             "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
+                +             "{ key: 'score' value: 0 } "
+                +             "{ key: 'ttlMillis' value: 0 } "
+                +             "{ key: 'uri' value: sDocumentProperties2 } ' ] } "
+                +       "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
+                +       "{ key: 'longKey1' value: [ '1' '2' '3' ] } "
+                +       "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] }  } "
+                + "{ key: 'schemaType' value: schemaType1 } "
+                + "{ key: 'score' value: 0 } "
+                + "{ key: 'ttlMillis' value: 0 } "
+                + "{ key: 'uri' value: uri1 } ";
+        assertThat(document.toString()).isEqualTo(exceptedString);
+    }
+
+    @Test
     public void testDocumentGetValues_DifferentTypes() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setScore(1)
                 .setProperty("longKey1", 1L)
                 .setProperty("booleanKey1", true, false, true)
@@ -213,53 +244,8 @@
 
     @Test
     public void testDocumentInvalid() {
-        AppSearchDocument.Builder builder = new AppSearchDocument.Builder("uri1", "schemaType1");
-        assertThrows(
+        GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1");
+        expectThrows(
                 IllegalArgumentException.class, () -> builder.setProperty("test", new boolean[]{}));
     }
-
-    @Test
-    public void testDocumentProtoPopulation() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setScore(1)
-                .setTtlMillis(1L)
-                .setProperty("longKey1", 1L)
-                .setProperty("doubleKey1", 1.0)
-                .setProperty("booleanKey1", true)
-                .setProperty("stringKey1", "test-value1")
-                .setProperty("byteKey1", sByteArray1)
-                .setProperty("documentKey1", sDocumentProperties1)
-                .build();
-
-        // Create the Document proto. Need to sort the property order by key.
-        DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
-                .setUri("uri1")
-                .setSchema("schemaType1")
-                .setCreationTimestampMs(5L)
-                .setScore(1)
-                .setTtlMs(1L)
-                .setNamespace("");
-        HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
-        propertyProtoMap.put("longKey1",
-                PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
-        propertyProtoMap.put("doubleKey1",
-                PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
-        propertyProtoMap.put("booleanKey1",
-                PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
-        propertyProtoMap.put("stringKey1",
-                PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
-        propertyProtoMap.put("byteKey1",
-                PropertyProto.newBuilder().setName("byteKey1").addBytesValues(
-                        ByteString.copyFrom(sByteArray1)));
-        propertyProtoMap.put("documentKey1",
-                PropertyProto.newBuilder().setName("documentKey1")
-                        .addDocumentValues(sDocumentProperties1.getProto()));
-        List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
-        Collections.sort(sortedKey);
-        for (int i = 0; i < sortedKey.size(); i++) {
-            documentProtoBuilder.addProperties(propertyProtoMap.get(sortedKey.get(i)));
-        }
-        assertThat(document.getProto()).isEqualTo(documentProtoBuilder.build());
-    }
 }
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java
new file mode 100644
index 0000000..acbf11a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 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.app.appsearch;
+
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+
+public class SearchResultsTest {
+    @Test
+    public void buildSearchSpecWithoutTermMatchType() {
+        expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
+                .setSchemaTypes("testSchemaType")
+                .build());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
similarity index 80%
rename from core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
index b29483c..2c7c35f 100644
--- a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,33 +14,30 @@
  * limitations under the License.
  */
 
-package android.app.appsearch.impl;
-
-import static com.google.common.truth.Truth.assertThat;
+package android.app.appsearch.customer;
 
 import android.annotation.NonNull;
-import android.app.appsearch.AppSearchDocument;
+import android.app.appsearch.GenericDocument;
 
-import androidx.test.filters.SmallTest;
+import static com.google.common.truth.Truth.assertThat;
 
 import org.junit.Test;
 
 /**
- * Tests that {@link AppSearchDocument} and {@link AppSearchDocument.Builder} are extendable by
+ * Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by
  * developers.
  *
- * <p>This class is intentionally in a different package than {@link AppSearchDocument} to make sure
+ * <p>This class is intentionally in a different package than {@link GenericDocument} to make sure
  * there are no package-private methods required for external developers to add custom types.
  */
-@SmallTest
 public class CustomerDocumentTest {
 
     private static byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
     private static byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
-    private static AppSearchDocument sDocumentProperties1 = new AppSearchDocument
+    private static GenericDocument sDocumentProperties1 = new GenericDocument
             .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
             .build();
-    private static AppSearchDocument sDocumentProperties2 = new AppSearchDocument
+    private static GenericDocument sDocumentProperties2 = new GenericDocument
             .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
             .build();
 
@@ -77,19 +74,21 @@
 
     /**
      * An example document type for test purposes, defined outside of
-     * {@link android.app.appsearch.AppSearch} (the way an external developer would define it).
+     * {@link GenericDocument} (the way an external developer would define
+     * it).
      */
-    private static class CustomerDocument extends AppSearchDocument {
-        private CustomerDocument(AppSearchDocument document) {
+    private static class CustomerDocument extends GenericDocument {
+        private CustomerDocument(GenericDocument document) {
             super(document);
         }
 
-        public static class Builder extends AppSearchDocument.Builder<CustomerDocument.Builder> {
+        public static class Builder extends GenericDocument.Builder<CustomerDocument.Builder> {
             private Builder(@NonNull String uri) {
                 super(uri, "customerDocument");
             }
 
             @Override
+            @NonNull
             public CustomerDocument build() {
                 return new CustomerDocument(super.build());
             }
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 285ad9f..4e49118 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -16,6 +16,8 @@
 
 package android.text;
 
+import static android.text.TextUtils.formatSimple;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -794,4 +796,53 @@
         assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3));
         assertNull(TextUtils.trimToLengthWithEllipsis(null, 3));
     }
+
+    @Test
+    public void testFormatSimple_Types() {
+        assertEquals("true", formatSimple("%b", true));
+        assertEquals("false", formatSimple("%b", false));
+        assertEquals("true", formatSimple("%b", this));
+        assertEquals("false", formatSimple("%b", new Object[] { null }));
+
+        assertEquals("!", formatSimple("%c", '!'));
+
+        assertEquals("42", formatSimple("%d", 42));
+        assertEquals("281474976710656", formatSimple("%d", 281474976710656L));
+
+        assertEquals("3.14159", formatSimple("%f", 3.14159));
+        assertEquals("NaN", formatSimple("%f", Float.NaN));
+
+        assertEquals("example", formatSimple("%s", "example"));
+        assertEquals("null", formatSimple("%s", new Object[] { null }));
+
+        assertEquals("2a", formatSimple("%x", 42));
+        assertEquals("1000000000000", formatSimple("%x", 281474976710656L));
+
+        assertEquals("%", formatSimple("%%"));
+    }
+
+    @Test
+    public void testFormatSimple_Empty() {
+        assertEquals("", formatSimple(""));
+    }
+
+    @Test
+    public void testFormatSimple_Typical() {
+        assertEquals("String foobar and %% number -42 together",
+                formatSimple("String %s%s and %%%% number %d%d together", "foo", "bar", -4, 2));
+    }
+
+    @Test
+    public void testFormatSimple_Mismatch() {
+        try {
+            formatSimple("%s");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            formatSimple("%s", "foo", "bar");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 8412e53..b98ce30 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -89,6 +90,7 @@
         mInsetsState = new InsetsState();
         mInsetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 500, 100));
         mInsetsState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
+        doNothing().when(mMockController).onRequestedVisibilityChanged(any());
         InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
                 () -> mMockTransaction, mMockController);
         topConsumer.setControl(
@@ -130,7 +132,7 @@
     public void testChangeInsets() {
         mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 1f /* alpha */,
                 0f /* fraction */);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
         assertEquals(1f, mController.getCurrentAlpha(), 1f - mController.getCurrentAlpha());
 
@@ -150,7 +152,7 @@
     public void testChangeAlphaNoInsets() {
         Insets initialInsets = mController.getCurrentInsets();
         mController.setInsetsAndAlpha(null, 0.5f, 0f /* fraction*/);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha());
         assertEquals(initialInsets, mController.getCurrentInsets());
     }
@@ -158,7 +160,7 @@
     @Test
     public void testChangeInsetsAndAlpha() {
         mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 0.5f, 1f);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha());
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
     }
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cc97eb5..7b84f68c 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -683,71 +683,15 @@
     @Test
     public void testRequestedState() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final InsetsState state = mTestHost.getRequestedState();
 
-            // The modified state can be controlled when we have control.
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            mController.hide(statusBars());
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+            mController.hide(statusBars() | navigationBars());
+            assertFalse(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
+            assertFalse(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
 
-            // The modified state won't be changed while losing control.
-            mController.onControlsChanged(null /* activeControls */);
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state won't be changed while state changed while we don't have control.
-            InsetsState newState = new InsetsState(mController.getState(), true /* copySource */);
-            mController.onStateChanged(newState);
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state won't be changed while controlling an insets without having the
-            // control.
-            mController.show(statusBars());
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state can be updated while gaining control.
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state can still be updated if the local state and the requested state
-            // are the same.
-            mController.onControlsChanged(null /* activeControls */);
-            mController.hide(statusBars());
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_STATUS_BAR).setVisible(false);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state will always be updated while receiving IME control if IME is
-            // requested visible.
-            mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */);
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_IME).setVisible(true);
-            newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
-            assertEquals(newState.getSource(ITYPE_IME),
-                    mTestHost.getModifiedState().peekSource(ITYPE_IME));
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_IME).setVisible(true);
-            newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
-            assertEquals(newState.getSource(ITYPE_IME),
-                    mTestHost.getModifiedState().peekSource(ITYPE_IME));
-
-            // The modified frames cannot be updated if there is an animation.
-            mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR));
-            mController.hide(navigationBars());
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--;
-            mController.onStateChanged(newState);
-            assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
-                    mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
-
-            // The modified frames can be updated while the animation is done.
-            mController.cancelExistingAnimations();
-            assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
-                    mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
+            mController.show(statusBars() | navigationBars());
+            assertTrue(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
+            assertTrue(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
         });
     }
 
@@ -878,7 +822,7 @@
 
     public static class TestHost extends ViewRootInsetsControllerHost {
 
-        private InsetsState mModifiedState = new InsetsState();
+        private final InsetsState mRequestedState = new InsetsState();
 
         TestHost(ViewRootImpl viewRoot) {
             super(viewRoot);
@@ -886,12 +830,12 @@
 
         @Override
         public void onInsetsModified(InsetsState insetsState) {
-            mModifiedState = new InsetsState(insetsState, true /* copySource */);
+            mRequestedState.set(insetsState, true);
             super.onInsetsModified(insetsState);
         }
 
-        public InsetsState getModifiedState() {
-            return mModifiedState;
+        public InsetsState getRequestedState() {
+            return mRequestedState;
         }
     }
 }
diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java
new file mode 100644
index 0000000..2dfc53a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+
+import java.util.Arrays;
+
+/**
+ * An {@link EditText} component that allows customizing its
+ * {@link android.view.inputmethod.InputConnection}.
+ */
+public class CustomInputConnectionEditText extends EditText {
+    private static final String LOG_TAG = "CustomInputConnectionEditText";
+
+    private String[] mContentMimeTypes;
+    private InputConnectionWrapper mInputConnectionWrapper;
+
+    public CustomInputConnectionEditText(Context context) {
+        super(context);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setContentMimeTypes(String[] contentMimeTypes) {
+        mContentMimeTypes = contentMimeTypes;
+    }
+
+    public void setInputConnectionWrapper(InputConnectionWrapper inputConnectionWrapper) {
+        mInputConnectionWrapper = inputConnectionWrapper;
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        InputConnection ic = super.onCreateInputConnection(outAttrs);
+        if (ic == null) {
+            Log.d(LOG_TAG, "Not wrapping InputConnection, because super returned null");
+            return null;
+        }
+        if (mInputConnectionWrapper == null) {
+            Log.d(LOG_TAG, "Not wrapping InputConnection, because wrapper is null");
+            return ic;
+        }
+
+        Log.d(LOG_TAG, "Wrapping InputConnection");
+        mInputConnectionWrapper.setTarget(ic);
+
+        Log.d(LOG_TAG,
+                "Setting EditorInfo.contentMimeTypes: " + Arrays.toString(mContentMimeTypes));
+        outAttrs.contentMimeTypes = mContentMimeTypes;
+
+        return mInputConnectionWrapper;
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java
new file mode 100644
index 0000000..9328a50
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 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.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * Activity that uses an {@link EditText} component that allows customizing its
+ * {@link android.view.inputmethod.InputConnection}.
+ */
+public class CustomInputConnectionEditTextActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_custom_input_connection_edit_text);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
new file mode 100644
index 0000000..5112326
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 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.widget;
+
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
+import static android.widget.TextViewOnReceiveContentCallback.canReuse;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.OnReceiveContentCallback;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Tests for {@link TextViewOnReceiveContentCallback}. Most of the test cases are in the CTS test
+ * {@link android.widget.cts.TextViewOnReceiveContentCallbackTest}. This class tests some internal
+ * implementation details, e.g. fallback to the keyboard image API.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class TextViewOnReceiveContentCallbackTest {
+    private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path");
+
+    @Rule
+    public ActivityTestRule<CustomInputConnectionEditTextActivity> mActivityRule =
+            new ActivityTestRule<>(CustomInputConnectionEditTextActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+    private CustomInputConnectionEditText mEditText;
+    private TextViewOnReceiveContentCallback mDefaultCallback;
+
+    @Before
+    public void before() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        mEditText = mActivity.findViewById(R.id.edittext2);
+        mDefaultCallback = mEditText.getEditorForTesting().getDefaultOnReceiveContentCallback();
+    }
+
+    @Test
+    public void testGetSupportedMimeTypes_fallbackToCommitContent() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Assert that the callback returns the MIME types declared in the EditorInfo in addition to
+        // the default.
+        assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly(
+                "text/*", "image/gif", "image/png");
+    }
+
+    @Test
+    public void testGetSupportedMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo()
+            throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[0]);
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Assert that the callback returns the default MIME types.
+        assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly("text/*");
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback with SOURCE_AUTOFILL and assert that it triggers a call to
+        // InputConnection.commitContent.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verify(ic.mMock, times(1))
+                .commitContent(any(InputContentInfo.class), eq(0), eq(null));
+        verifyNoMoreInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent_noMimeTypesInEditorInfo() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[0]);
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback and assert that the InputConnection is not invoked.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent_sourceOtherThanAutofill() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback with sources other than SOURCE_AUTOFILL and assert that it does NOT
+        // trigger calls to InputConnection.commitContent.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testCanReuse() throws Throwable {
+        ArraySet<String> mimeTypes = null;
+        String[] editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = new ArraySet<>();
+        editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("text/*");
+        editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("text/*");
+        editorContentMimeTypes = new String[] {"text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png", "text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png", "image/jpg"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/jpg"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/jpg", "text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+    }
+
+    private static class MyInputConnection extends InputConnectionWrapper {
+        public final InputConnection mMock;
+
+        MyInputConnection() {
+            super(null, true);
+            mMock = Mockito.mock(InputConnection.class);
+        }
+
+        @Override
+        public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+            mMock.commitContent(inputContentInfo, flags, opts);
+            return true;
+        }
+    }
+
+    @SafeVarargs
+    private static <T> ArraySet<T> newArraySet(T ... elements) {
+        return new ArraySet<>(elements);
+    }
+}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 59aa45e..3a20a9c 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -44,5 +44,8 @@
         <!-- use for CarServiceTest -->
         <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+
+        <!-- use for rotary fragment to enable/disable packages related to rotary -->
+        <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
     </privapp-permissions>
 </permissions>
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
new file mode 100644
index 0000000..3fbd51d
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2020 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.kindIs;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.not;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.predicates.TypePredicate;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream {@link String}
+ * operations.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkEfficientStrings",
+    summary = "Verifies efficient Strings best-practices",
+    severity = WARNING)
+public final class EfficientStringsChecker extends BugChecker
+        implements MethodInvocationTreeMatcher {
+
+    private static final Matcher<ExpressionTree> FORMAT_CALL = methodInvocation(
+            staticMethod().onClass("java.lang.String").named("format"));
+    private static final Matcher<ExpressionTree> PRECONDITIONS_CALL = methodInvocation(
+            staticMethod().onClass(withSimpleName("Preconditions")).withAnyName());
+    private static final Matcher<ExpressionTree> OBJECTS_CALL = methodInvocation(
+            staticMethod().onClass("java.util.Objects").named("requireNonNull"));
+
+    /**
+     * Match an expression which combines both string literals any other dynamic
+     * values, since these allocate a transparent StringBuilder.
+     * <p>
+     * This won't match a single isolated string literal, or a chain consisting
+     * of only string literals, since those don't require dynamic construction.
+     */
+    private static final Matcher<ExpressionTree> CONTAINS_DYNAMIC_STRING = allOf(
+            contains(ExpressionTree.class, kindIs(Kind.STRING_LITERAL)),
+            contains(ExpressionTree.class, not(kindIs(Kind.STRING_LITERAL))));
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (FORMAT_CALL.matches(tree, state)) {
+            // Skip over possible locale to find format string
+            final List<? extends ExpressionTree> args = tree.getArguments();
+            final ExpressionTree formatArg;
+            final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
+            if (vars.get(0).type.toString().equals("java.util.Locale")) {
+                formatArg = args.get(1);
+            } else {
+                formatArg = args.get(0);
+            }
+
+            // Determine if format string is "simple" enough to replace
+            if (formatArg.getKind() == Kind.STRING_LITERAL) {
+                final String format = String.valueOf(((LiteralTree) formatArg).getValue());
+                if (isSimple(format)) {
+                    return buildDescription(formatArg)
+                            .setMessage("Simple format strings can be replaced with "
+                                    + "TextUtils.formatSimple() for a 6x performance improvement")
+                            .build();
+                }
+            }
+        } else if (PRECONDITIONS_CALL.matches(tree, state)
+                || OBJECTS_CALL.matches(tree, state)) {
+            final List<? extends ExpressionTree> args = tree.getArguments();
+            for (int i = 1 ; i < args.size(); i++) {
+                final ExpressionTree arg = args.get(i);
+                if (CONTAINS_DYNAMIC_STRING.matches(arg, state)) {
+                    return buildDescription(arg)
+                            .setMessage("Building dynamic messages is discouraged, since they "
+                                    + "always allocate a transparent StringBuilder, even in "
+                                    + "the successful case")
+                            .build();
+                }
+            }
+        }
+        return Description.NO_MATCH;
+    }
+
+    static boolean isSimple(String format) {
+        for (int i = 0; i < format.length(); i++) {
+            char c = format.charAt(i);
+            if (c == '%') {
+                i++;
+                c = format.charAt(i);
+                switch (c) {
+                    case 'b':
+                    case 'c':
+                    case 'd':
+                    case 'f':
+                    case 's':
+                    case 'x':
+                    case '%':
+                        break;
+                    default:
+                        return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    static TypePredicate withSimpleName(final String filter) {
+        return new TypePredicate() {
+            @Override
+            public boolean apply(Type type, VisitorState state) {
+                return type.tsym.getSimpleName().toString().equals(filter);
+            }
+        };
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
new file mode 100644
index 0000000..a755564
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 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.google.errorprone.bugpatterns.android;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientStringsCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                EfficientStringsChecker.class, getClass());
+    }
+
+    @Test
+    public void testSimple() {
+        assertTrue(EfficientStringsChecker.isSimple(""));
+        assertTrue(EfficientStringsChecker.isSimple("%s"));
+        assertTrue(EfficientStringsChecker.isSimple("String %s%s and %%%% number %d%d together"));
+
+        assertFalse(EfficientStringsChecker.isSimple("%04d"));
+        assertFalse(EfficientStringsChecker.isSimple("%02x:%02x:%02x"));
+    }
+
+    @Test
+    public void testFormat() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.Locale;",
+                        "public class Example {",
+                        "  public void example(String str) {",
+                        "    String.format(str, str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(\"foo %s bar\", str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(\"foo %d bar\", 42);",
+                        "    String.format(\"foo %04d bar\", 42);",
+                        "  }",
+                        "  public void exampleLocale(String str) {",
+                        "    String.format(Locale.US, str, str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(Locale.US, \"foo %s bar\", str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(Locale.US, \"foo %d bar\", 42);",
+                        "    String.format(Locale.US, \"foo %04d bar\", 42);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testPreconditions() {
+        compilationHelper
+                .addSourceFile("/android/util/Preconditions.java")
+                .addSourceLines("Example.java",
+                        "import android.util.Preconditions;",
+                        "import java.util.Objects;",
+                        "public class Example {",
+                        "  String str;",
+                        "  public void checkState(boolean val) {",
+                        "    Preconditions.checkState(val);",
+                        "    Preconditions.checkState(val, str);",
+                        "    Preconditions.checkState(val, \"foo\");",
+                        "    Preconditions.checkState(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkState(val, \"foo \" + val);",
+                        "  }",
+                        "  public void checkArgument(boolean val) {",
+                        "    Preconditions.checkArgument(val);",
+                        "    Preconditions.checkArgument(val, str);",
+                        "    Preconditions.checkArgument(val, \"foo\");",
+                        "    Preconditions.checkArgument(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkArgument(val, \"foo \" + val);",
+                        "  }",
+                        "  public void checkNotNull(Object val) {",
+                        "    Preconditions.checkNotNull(val);",
+                        "    Preconditions.checkNotNull(val, str);",
+                        "    Preconditions.checkNotNull(val, \"foo\");",
+                        "    Preconditions.checkNotNull(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo \" + val);",
+                        "  }",
+                        "  public void requireNonNull(Object val) {",
+                        "    Objects.requireNonNull(val);",
+                        "    Objects.requireNonNull(val, str);",
+                        "    Objects.requireNonNull(val, \"foo\");",
+                        "    Objects.requireNonNull(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Objects.requireNonNull(val, \"foo \" + val);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/res/android/util/Preconditions.java b/errorprone/tests/res/android/util/Preconditions.java
new file mode 100644
index 0000000..558cdaf
--- /dev/null
+++ b/errorprone/tests/res/android/util/Preconditions.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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;
+
+public class Preconditions {
+    public static void checkArgument(boolean expression) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkArgument(boolean expression, Object errorMessage) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static <T> T checkNotNull(T reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static <T> T checkNotNull(T reference, Object errorMessage) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkState(boolean expression) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkState(boolean expression, String message) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4c0f890..8284042 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -32,6 +32,7 @@
     private static native Surface nativeGetSurface(long ptr);
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+    private static native void nativeFlushShadowQueue(long ptr);
 
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -69,4 +70,8 @@
             super.finalize();
         }
     }
+
+    public void flushShadowQueue() {
+        nativeFlushShadowQueue(mNativeObject);
+    }
 }
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 97cd8ab..586c512 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -523,6 +523,9 @@
     /**
      * Returns a font file buffer.
      *
+     * Duplicate before reading values by {@link ByteBuffer#duplicate()} for avoiding unexpected
+     * reading position sharing.
+     *
      * @return a font buffer
      */
     public @NonNull ByteBuffer getBuffer() {
@@ -628,18 +631,49 @@
         if (o == this) {
             return true;
         }
-        if (o == null || !(o instanceof Font)) {
+        if (!(o instanceof Font)) {
             return false;
         }
         Font f = (Font) o;
-        return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
-                && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer)
-                && Objects.equals(f.mLocaleList, mLocaleList);
+        boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
+                && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
+                && Objects.equals(mFile, f.mFile);
+
+        if (!paramEqual) {
+            return false;
+        }
+
+        // Shortcut for different font buffer check by comparing size.
+        if (mBuffer.capacity() != f.mBuffer.capacity()) {
+            return false;
+        }
+
+        // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
+        // underlying native font object holds buffer address, check if this buffer points exactly
+        // the same address as a shortcut of equality. For being compatible with of API30 or before,
+        // check buffer position even if the buffer points the same address.
+        if (nIsSameBufferAddress(mNativePtr, f.mNativePtr)
+                && mBuffer.position() == f.mBuffer.position()) {
+            return true;
+        }
+
+        // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
+        // file but has the same file size, or two font has same content but they are allocated
+        // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
+        return mBuffer.equals(f.mBuffer);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer, mLocaleList);
+        return Objects.hash(
+                mFontStyle,
+                mTtcIndex,
+                Arrays.hashCode(mAxes),
+                // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
+                // data which is not performant e.g. for HashMap. The hash collision are less likely
+                // happens because it is unlikely happens the different font files has exactly the
+                // same size.
+                mLocaleList);
     }
 
     @Override
@@ -724,4 +758,7 @@
 
     @CriticalNative
     private static native long nGetNativeFontPtr(long ptr);
+
+    @CriticalNative
+    private static native boolean nIsSameBufferAddress(long lFontPtr, long rFontPtr);
 }
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 44744bc..3f6ca0f 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -1,12 +1,6 @@
 {
   "version": "1.0.0",
   "messages": {
-    "-1823823103": {
-      "message": "Add listener for types=%s listener=%s",
-      "level": "VERBOSE",
-      "group": "WM_SHELL_TASK_ORG",
-      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
-    },
     "-1683614271": {
       "message": "Existing task: id=%d component=%s",
       "level": "VERBOSE",
@@ -61,6 +55,12 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "481673835": {
+      "message": "addListenerForTaskId taskId=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
     "564235578": {
       "message": "Fullscreen Task Vanished: #%d",
       "level": "VERBOSE",
@@ -78,6 +78,12 @@
       "level": "VERBOSE",
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "1990759023": {
+      "message": "addListenerForType types=%s listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     }
   },
   "groups": {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 9d6271b..4cb5fd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -16,17 +16,24 @@
 
 package com.android.wm.shell;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+
 import android.app.ActivityManager;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.view.SurfaceControl;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
+import java.io.PrintWriter;
+
 class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
-    private static final String TAG = "FullscreenTaskOrg";
+    private static final String TAG = "FullscreenTaskListener";
 
     private final SyncTransactionQueue mSyncQueue;
 
@@ -74,6 +81,15 @@
     }
 
     @Override
-    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+        pw.println(innerPrefix + mTasks.size() + " Tasks");
+    }
+
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index d87de5a..9f3c83c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -24,9 +24,13 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
 import android.annotation.IntDef;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration.WindowingMode;
+import android.os.IBinder;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
@@ -43,6 +47,8 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -79,14 +85,23 @@
         default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
         default void onTaskVanished(RunningTaskInfo taskInfo) {}
         default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
+        default void dump(@NonNull PrintWriter pw, String prefix) {};
     }
 
-    private final SparseArray<TaskListener> mTaskListenersByType = new SparseArray<>();
+    /**
+     * Keys map from either a task id or {@link TaskListenerType}.
+     * @see #addListenerForTaskId
+     * @see #addListenerForType
+     */
+    private final SparseArray<TaskListener> mTaskListeners = new SparseArray<>();
 
     // Keeps track of all the tasks reported to this organizer (changes in windowing mode will
     // require us to report to both old and new listeners)
     private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>();
 
+    /** @see #setPendingLaunchCookieListener */
+    private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>();
+
     // TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
     private final Transitions mTransitions;
 
@@ -100,7 +115,7 @@
             SyncTransactionQueue syncQueue, TransactionPool transactionPool,
             ShellExecutor mainExecutor, ShellExecutor animExecutor) {
         super(taskOrganizerController);
-        addListener(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
+        addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
         mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
     }
@@ -119,26 +134,43 @@
     }
 
     /**
+     * Adds a listener for a specific task id.
+     */
+    public void addListenerForTaskId(TaskListener listener, int taskId) {
+        ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
+        if (mTaskListeners.get(taskId) != null) {
+            throw new IllegalArgumentException("Listener for taskId=" + taskId + " already exists");
+        }
+
+        final TaskAppearedInfo info = mTasks.get(taskId);
+        if (info == null) {
+            throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
+        }
+
+        final TaskListener oldListener = getTaskListener(info.getTaskInfo());
+        mTaskListeners.put(taskId, listener);
+        updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+    }
+
+    /**
      * Adds a listener for tasks with given types.
      */
-    public void addListener(TaskListener listener, @TaskListenerType int... taskListenerTypes) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for types=%s listener=%s",
-                Arrays.toString(taskListenerTypes), listener);
-        for (int listenerType : taskListenerTypes) {
-            if (mTaskListenersByType.get(listenerType) != null) {
+    public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) {
+        ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
+                Arrays.toString(listenerTypes), listener);
+        for (int listenerType : listenerTypes) {
+            if (mTaskListeners.get(listenerType) != null) {
                 throw new IllegalArgumentException("Listener for listenerType=" + listenerType
                         + " already exists");
             }
-            mTaskListenersByType.put(listenerType, listener);
+            mTaskListeners.put(listenerType, listener);
 
             // Notify the listener of all existing tasks with the given type.
-            for (int i = mTasks.size() - 1; i >= 0; i--) {
-                TaskAppearedInfo data = mTasks.valueAt(i);
-                final @TaskListenerType int taskListenerType = getTaskListenerType(
-                        data.getTaskInfo());
-                if (taskListenerType == listenerType) {
-                    listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
-                }
+            for (int i = mTasks.size() - 1; i >= 0; --i) {
+                final TaskAppearedInfo data = mTasks.valueAt(i);
+                final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+                if (taskListener != listener) continue;
+                listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
             }
         }
     }
@@ -147,21 +179,47 @@
      * Removes a registered listener.
      */
     public void removeListener(TaskListener listener) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
-        final int index = mTaskListenersByType.indexOfValue(listener);
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+        final int index = mTaskListeners.indexOfValue(listener);
         if (index == -1) {
             Log.w(TAG, "No registered listener found");
             return;
         }
-        mTaskListenersByType.removeAt(index);
+
+        // Collect tasks associated with the listener we are about to remove.
+        final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskAppearedInfo data = mTasks.valueAt(i);
+            final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+            if (taskListener != listener) continue;
+            tasks.add(data);
+        }
+
+        // Remove listener
+        mTaskListeners.removeAt(index);
+
+        // Associate tasks with new listeners if needed.
+        for (int i = tasks.size() - 1; i >= 0; --i) {
+            final TaskAppearedInfo data = tasks.get(i);
+            updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
+                    null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+        }
+    }
+
+    /**
+     * Associated a listener to a pending launch cookie so we can route the task later once it
+     * appears.
+     */
+    public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
+        mLaunchCookieToListener.put(cookie, listener);
     }
 
     @Override
     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
-                taskInfo.taskId);
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d", taskInfo.taskId);
         mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, leash));
-        final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
+        final TaskListener listener =
+                getTaskListener(taskInfo, true /*removeLaunchCookieIfNeeded*/);
         if (listener != null) {
             listener.onTaskAppeared(taskInfo, leash);
         }
@@ -169,37 +227,22 @@
 
     @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
-                taskInfo.taskId);
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
         final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
-        final @TaskListenerType int listenerType = getTaskListenerType(taskInfo);
-        final @TaskListenerType int prevListenerType = getTaskListenerType(data.getTaskInfo());
+        final TaskListener oldListener = getTaskListener(data.getTaskInfo());
+        final TaskListener newListener = getTaskListener(taskInfo);
         mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
-        if (prevListenerType != listenerType) {
-            // TODO: We currently send vanished/appeared as the task moves between types, but
-            //       we should consider adding a different mode-changed callback
-            TaskListener listener = mTaskListenersByType.get(prevListenerType);
-            if (listener != null) {
-                listener.onTaskVanished(taskInfo);
-            }
-            listener = mTaskListenersByType.get(listenerType);
-            if (listener != null) {
-                SurfaceControl leash = data.getLeash();
-                listener.onTaskAppeared(taskInfo, leash);
-            }
-        } else {
-            final TaskListener listener = mTaskListenersByType.get(listenerType);
-            if (listener != null) {
-                listener.onTaskInfoChanged(taskInfo);
-            }
+        final boolean updated = updateTaskListenerIfNeeded(
+                taskInfo, data.getLeash(), oldListener, newListener);
+        if (!updated && newListener != null) {
+            newListener.onTaskInfoChanged(taskInfo);
         }
     }
 
     @Override
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
-                taskInfo.taskId);
-        final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
+        final TaskListener listener = getTaskListener(taskInfo);
         if (listener != null) {
             listener.onBackPressedOnTaskRoot(taskInfo);
         }
@@ -207,22 +250,72 @@
 
     @Override
     public void onTaskVanished(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
-                taskInfo.taskId);
-        final @TaskListenerType int prevListenerType =
-                getTaskListenerType(mTasks.get(taskInfo.taskId).getTaskInfo());
-        mTasks.remove(taskInfo.taskId);
-        final TaskListener listener = mTaskListenersByType.get(prevListenerType);
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+        final int taskId = taskInfo.taskId;
+        final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
+        mTasks.remove(taskId);
         if (listener != null) {
             listener.onTaskVanished(taskInfo);
         }
     }
 
-    @TaskListenerType
-    private static int getTaskListenerType(RunningTaskInfo runningTaskInfo) {
-        // Right now it's N:1 mapping but in the future different task listerners
-        // may be triggered by one windowing mode depending on task parameters.
-        switch (getWindowingMode(runningTaskInfo)) {
+    private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
+            TaskListener oldListener, TaskListener newListener) {
+        if (oldListener == newListener) return false;
+        // TODO: We currently send vanished/appeared as the task moves between types, but
+        //       we should consider adding a different mode-changed callback
+        if (oldListener != null) {
+            oldListener.onTaskVanished(taskInfo);
+        }
+        if (newListener != null) {
+            newListener.onTaskAppeared(taskInfo, leash);
+        }
+        return true;
+    }
+
+    private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
+        return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/);
+    }
+
+    private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo,
+            boolean removeLaunchCookieIfNeeded) {
+
+        final int taskId = runningTaskInfo.taskId;
+        TaskListener listener;
+
+        // First priority goes to listener that might be pending for this task.
+        final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies;
+        for (int i = launchCookies.size() - 1; i >= 0; --i) {
+            final IBinder cookie = launchCookies.get(i);
+            listener = mLaunchCookieToListener.get(cookie);
+            if (listener == null) continue;
+
+            if (removeLaunchCookieIfNeeded) {
+                // Remove the cookie and add the listener.
+                mLaunchCookieToListener.remove(cookie);
+                mTaskListeners.put(taskId, listener);
+            }
+            return listener;
+        }
+
+        // Next priority goes to taskId specific listeners.
+        listener = mTaskListeners.get(taskId);
+        if (listener != null) return listener;
+
+        // Next we try type specific listeners.
+        final int windowingMode = getWindowingMode(runningTaskInfo);
+        final int taskListenerType = windowingModeToTaskListenerType(windowingMode);
+        return mTaskListeners.get(taskListenerType);
+    }
+
+    @WindowingMode
+    private static int getWindowingMode(RunningTaskInfo taskInfo) {
+        return taskInfo.configuration.windowConfiguration.getWindowingMode();
+    }
+
+    private static @TaskListenerType int windowingModeToTaskListenerType(
+            @WindowingMode int windowingMode) {
+        switch (windowingMode) {
             case WINDOWING_MODE_FULLSCREEN:
                 return TASK_LISTENER_TYPE_FULLSCREEN;
             case WINDOWING_MODE_MULTI_WINDOW:
@@ -239,8 +332,50 @@
         }
     }
 
-    @WindowingMode
-    private static int getWindowingMode(RunningTaskInfo taskInfo) {
-        return taskInfo.configuration.windowConfiguration.getWindowingMode();
+    public static String taskListenerTypeToString(@TaskListenerType int type) {
+        switch (type) {
+            case TASK_LISTENER_TYPE_FULLSCREEN:
+                return "TASK_LISTENER_TYPE_FULLSCREEN";
+            case TASK_LISTENER_TYPE_MULTI_WINDOW:
+                return "TASK_LISTENER_TYPE_MULTI_WINDOW";
+            case TASK_LISTENER_TYPE_SPLIT_SCREEN:
+                return "TASK_LISTENER_TYPE_SPLIT_SCREEN";
+            case TASK_LISTENER_TYPE_PIP:
+                return "TASK_LISTENER_TYPE_PIP";
+            case TASK_LISTENER_TYPE_UNDEFINED:
+                return "TASK_LISTENER_TYPE_UNDEFINED";
+            default:
+                return "taskId#" + type;
+        }
+    }
+
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
+        for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+            final int key = mTaskListeners.keyAt(i);
+            final TaskListener listener = mTaskListeners.valueAt(i);
+            pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
+            listener.dump(pw, childPrefix);
+        }
+
+        pw.println();
+        pw.println(innerPrefix + mTasks.size() + " Tasks");
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final int key = mTasks.keyAt(i);
+            final TaskAppearedInfo info = mTasks.valueAt(i);
+            final TaskListener listener = getTaskListener(info.getTaskInfo());
+            pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
+        }
+
+        pw.println();
+        pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
+        for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
+            final IBinder key = mLaunchCookieToListener.keyAt(i);
+            final TaskListener listener = mLaunchCookieToListener.valueAt(i);
+            pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index d810fb8..ea18a19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -69,11 +69,6 @@
     private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
     private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
 
-    @Deprecated
-    public DisplayImeController(IWindowManager wmService, DisplayController displayController,
-            Handler mainHandler, TransactionPool transactionPool) {
-        this(wmService, displayController, mainHandler::post, transactionPool);
-    }
 
     public DisplayImeController(IWindowManager wmService, DisplayController displayController,
             Executor mainExecutor, TransactionPool transactionPool) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 488f909..3ded409 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -98,7 +98,7 @@
     /**
      * Hides the PIP menu.
      */
-    void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback);
+    default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
 
     /**
      * Returns {@code true} if PIP is shown.
@@ -226,7 +226,7 @@
     /**
      * Called when showing Pip menu.
      */
-    void showPictureInPictureMenu();
+    default void showPictureInPictureMenu() {}
 
     /**
      * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
index de3261b..2ab087c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
@@ -22,9 +22,9 @@
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 
+import android.annotation.NonNull;
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -52,14 +52,11 @@
     private static final String TAG = PipBoundsHandler.class.getSimpleName();
     private static final float INVALID_SNAP_FRACTION = -1f;
 
+    private final @NonNull PipBoundsState mPipBoundsState;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private DisplayLayout mDisplayLayout;
 
-    private ComponentName mLastPipComponentName;
-    private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
-    private Size mReentrySize;
-
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
@@ -75,7 +72,8 @@
     private boolean mIsShelfShowing;
     private int mShelfHeight;
 
-    public PipBoundsHandler(Context context) {
+    public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) {
+        mPipBoundsState = pipBoundsState;
         mSnapAlgorithm = new PipSnapAlgorithm(context);
         mDisplayLayout = new DisplayLayout();
         reloadResources(context);
@@ -175,40 +173,6 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on saving reentry snap fraction and size
-     * for a given {@link ComponentName}.
-     */
-    public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
-        mReentrySnapFraction = getSnapFraction(bounds);
-        mReentrySize = new Size(bounds.width(), bounds.height());
-        mLastPipComponentName = componentName;
-    }
-
-    /**
-     * Responds to IPinnedStackListener on resetting reentry snap fraction and size
-     * for a given {@link ComponentName}.
-     */
-    public void onResetReentryBounds(ComponentName componentName) {
-        if (componentName.equals(mLastPipComponentName)) {
-            onResetReentryBoundsUnchecked();
-        }
-    }
-
-    private void onResetReentryBoundsUnchecked() {
-        mReentrySnapFraction = INVALID_SNAP_FRACTION;
-        mReentrySize = null;
-        mLastPipComponentName = null;
-    }
-
-    /**
-     * Returns ture if there's a valid snap fraction. This is used with {@link EXTRA_IS_FIRST_ENTRY}
-     * to see if this is the first time user has entered PIP for the component.
-     */
-    public boolean hasSaveReentryBounds() {
-        return mReentrySnapFraction != INVALID_SNAP_FRACTION;
-    }
-
-    /**
      * The {@link PipSnapAlgorithm} is couple on display bounds
      * @return {@link PipSnapAlgorithm}.
      */
@@ -250,37 +214,43 @@
     }
 
     /**
-     * See {@link #getDestinationBounds(ComponentName, float, Rect, Size, boolean)}
+     * See {@link #getDestinationBounds(float, Rect, Size, boolean)}
      */
-    public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
-            Size minimalSize) {
-        return getDestinationBounds(componentName, aspectRatio, bounds, minimalSize,
+    public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
+        return getDestinationBounds(aspectRatio, bounds, minimalSize,
                 false /* useCurrentMinEdgeSize */);
     }
 
     /**
      * @return {@link Rect} of the destination PiP window bounds.
      */
-    public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
+    public Rect getDestinationBounds(float aspectRatio, Rect bounds,
             Size minimalSize, boolean useCurrentMinEdgeSize) {
-        if (!componentName.equals(mLastPipComponentName)) {
-            onResetReentryBoundsUnchecked();
-            mLastPipComponentName = componentName;
-        }
+        boolean isReentryBounds = false;
         final Rect destinationBounds;
         if (bounds == null) {
-            final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
-            destinationBounds = new Rect(defaultBounds);
-            if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
+            // Calculating initial entry bounds
+            final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+
+            final Rect defaultBounds;
+            if (state != null) {
+                // Restore to reentry bounds.
+                defaultBounds = getDefaultBounds(state.getSnapFraction(), state.getSize());
+                isReentryBounds = true;
+            } else {
+                // Get actual default bounds.
+                defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
                 mOverrideMinimalSize = minimalSize;
             }
+
+            destinationBounds = new Rect(defaultBounds);
         } else {
+            // Just adjusting bounds (e.g. on aspect ratio changed).
             destinationBounds = new Rect(bounds);
         }
         if (isValidPictureInPictureAspectRatio(aspectRatio)) {
-            boolean useCurrentSize = bounds == null && mReentrySize != null;
             transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize,
-                    useCurrentSize);
+                    isReentryBounds);
         }
         mAspectRatio = aspectRatio;
         return destinationBounds;
@@ -533,9 +503,6 @@
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
-        pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
-        pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
-        pw.println(innerPrefix + "mReentrySize=" + mReentrySize);
         pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
         pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
         pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 10e5c3d..2625f16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -17,9 +17,15 @@
 package com.android.wm.shell.pip;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.graphics.Rect;
+import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * Singleton source of truth for the current state of PIP bounds.
@@ -28,6 +34,8 @@
     private static final String TAG = PipBoundsState.class.getSimpleName();
 
     private final @NonNull Rect mBounds = new Rect();
+    private PipReentryState mPipReentryState;
+    private ComponentName mLastPipComponentName;
 
     void setBounds(@NonNull Rect bounds) {
         mBounds.set(bounds);
@@ -39,11 +47,83 @@
     }
 
     /**
+     * Save the reentry state to restore to when re-entering PIP mode.
+     *
+     * TODO(b/169373982): consider refactoring this so that this class alone can use mBounds and
+     * calculate the snap fraction to save for re-entry.
+     */
+    public void saveReentryState(@NonNull Rect bounds, float fraction) {
+        mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction);
+    }
+
+    /**
+     * Returns the saved reentry state.
+     */
+    @Nullable
+    public PipReentryState getReentryState() {
+        return mPipReentryState;
+    }
+
+    /**
+     * Set the last {@link ComponentName} to enter PIP mode.
+     */
+    public void setLastPipComponentName(ComponentName lastPipComponentName) {
+        final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
+        mLastPipComponentName = lastPipComponentName;
+        if (changed) {
+            clearReentryState();
+        }
+    }
+
+    public ComponentName getLastPipComponentName() {
+        return mLastPipComponentName;
+    }
+
+    @VisibleForTesting
+    void clearReentryState() {
+        mPipReentryState = null;
+    }
+
+    static final class PipReentryState {
+        private static final String TAG = PipReentryState.class.getSimpleName();
+
+        private final @NonNull Size mSize;
+        private final float mSnapFraction;
+
+        PipReentryState(@NonNull Size size, float snapFraction) {
+            mSize = size;
+            mSnapFraction = snapFraction;
+        }
+
+        @NonNull
+        Size getSize() {
+            return mSize;
+        }
+
+        float getSnapFraction() {
+            return mSnapFraction;
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            final String innerPrefix = prefix + "  ";
+            pw.println(prefix + TAG);
+            pw.println(innerPrefix + "mSize=" + mSize);
+            pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
+        }
+    }
+
+    /**
      * Dumps internal state.
      */
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mBounds=" + mBounds);
+        pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
+        if (mPipReentryState == null) {
+            pw.println(innerPrefix + "mPipReentryState=null");
+        } else {
+            mPipReentryState.dump(pw, innerPrefix);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 15fd424..6990186 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -67,6 +68,7 @@
 import com.android.wm.shell.pip.phone.PipMenuActivityController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipUpdateThread;
+import com.android.wm.shell.pip.phone.PipUtils;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.PrintWriter;
@@ -280,7 +282,7 @@
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mSplitScreenOptional = splitScreenOptional;
         mTaskOrganizer = shellTaskOrganizer;
-        mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_PIP);
+        mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
         displayController.addDisplayWindowListener(this);
     }
 
@@ -329,7 +331,8 @@
             PictureInPictureParams pictureInPictureParams) {
         mShouldIgnoreEnteringPipTransition = true;
         mState = State.ENTERING_PIP;
-        return mPipBoundsHandler.getDestinationBounds(componentName,
+        mPipBoundsState.setLastPipComponentName(componentName);
+        return mPipBoundsHandler.getDestinationBounds(
                 getAspectRatioOrDefault(pictureInPictureParams),
                 null /* bounds */, getMinimalSize(activityInfo));
     }
@@ -465,6 +468,7 @@
         mLeash = leash;
         mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
+        mPipBoundsState.setLastPipComponentName(mTaskInfo.topActivity);
 
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
         mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
@@ -491,7 +495,7 @@
         }
 
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
+                getAspectRatioOrDefault(mPictureInPictureParams),
                 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -686,13 +690,14 @@
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
+        mPipBoundsState.setLastPipComponentName(info.topActivity);
         final PictureInPictureParams newParams = info.pictureInPictureParams;
         if (newParams == null || !applyPictureInPictureParams(newParams)) {
             Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
             return;
         }
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                info.topActivity, getAspectRatioOrDefault(newParams),
+                getAspectRatioOrDefault(newParams),
                 mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
                 true /* userCurrentMinEdgeSize */);
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
@@ -709,7 +714,7 @@
     public void onFixedRotationFinished(int displayId) {
         if (mShouldDeferEnteringPip && mState.isInPip()) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
+                    getAspectRatioOrDefault(mPictureInPictureParams),
                     null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
             // schedule a regular animation to ensure all the callbacks are still being sent
             enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
@@ -783,7 +788,7 @@
         }
 
         final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
+                getAspectRatioOrDefault(mPictureInPictureParams),
                 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
         if (newDestinationBounds.equals(currentDestinationBounds)) return;
         if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
@@ -1123,6 +1128,7 @@
     /**
      * Dumps internal states.
      */
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
@@ -1140,6 +1146,11 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP);
+    }
+
     /**
      * Callback interface for PiP transitions (both from and to PiP mode)
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 13f5ac3..a3d21f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -173,7 +173,13 @@
 
         @Override
         public void onActivityHidden(ComponentName componentName) {
-            mHandler.post(() -> mPipBoundsHandler.onResetReentryBounds(componentName));
+            mHandler.post(() -> {
+                if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
+                    // The activity was removed, we don't want to restore to the reentry state
+                    // saved for this component anymore.
+                    mPipBoundsState.setLastPipComponentName(null);
+                }
+            });
         }
 
         @Override
@@ -384,7 +390,8 @@
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry bounds to restore to when re-entering.
             updateReentryBounds(pipBounds);
-            mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds);
+            final float snapFraction = mPipBoundsHandler.getSnapFraction(mReentryBounds);
+            mPipBoundsState.saveReentryState(mReentryBounds, snapFraction);
         }
         // Disable touches while the animation is running
         mTouchHandler.setTouchEnabled(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
index 4a8db6b..22c05fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
@@ -28,7 +28,6 @@
 import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.media.session.MediaController;
-import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.UserHandle;
@@ -153,8 +152,7 @@
         }
 
         ArrayList<RemoteAction> mediaActions = new ArrayList<>();
-        int state = mMediaController.getPlaybackState().getState();
-        boolean isPlaying = MediaSession.isActiveState(state);
+        boolean isPlaying = mMediaController.getPlaybackState().isActiveState();
         long actions = mMediaController.getPlaybackState().getActions();
 
         // Prev action
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
index 8660702..f763d6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
@@ -24,6 +24,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.graphics.Rect;
@@ -33,8 +34,12 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
+import androidx.annotation.NonNull;
+
 import com.android.wm.shell.ShellTaskOrganizer;
 
+import java.io.PrintWriter;
+
 class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
     private static final String TAG = "SplitScreenTaskOrg";
     private static final boolean DEBUG = SplitScreenController.DEBUG;
@@ -57,7 +62,7 @@
                     ShellTaskOrganizer shellTaskOrganizer) {
         mSplitScreenController = splitScreenController;
         mTaskOrganizer = shellTaskOrganizer;
-        mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_SPLIT_SCREEN);
+        mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_SPLIT_SCREEN);
     }
 
     void init() throws RemoteException {
@@ -229,4 +234,16 @@
             mSplitScreenController.ensureNormalSplit();
         }
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+    }
+
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_SPLIT_SCREEN);
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 5418a5b..07a6bda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -24,8 +24,8 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -34,6 +34,8 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
@@ -89,11 +91,6 @@
         public void onTaskVanished(RunningTaskInfo taskInfo) {
             vanished.add(taskInfo);
         }
-
-        @Override
-        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-            // Not currently used
-        }
     }
 
     @Before
@@ -116,9 +113,10 @@
 
     @Test
     public void testOneListenerPerType() {
-        mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
         try {
-            mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
+            mOrganizer.addListenerForType(
+                    new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
             fail("Expected exception due to already registered listener");
         } catch (Exception e) {
             // Expected failure
@@ -141,7 +139,7 @@
 
         // Check that the tasks are next reported when the listener is added
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         assertTrue(listener.appeared.contains(task1));
         assertTrue(listener.appeared.contains(task2));
     }
@@ -150,7 +148,7 @@
     public void testAppearedVanished() {
         RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(listener.appeared.contains(taskInfo));
 
@@ -164,7 +162,7 @@
         mOrganizer.onTaskAppeared(taskInfo, null);
 
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         assertTrue(listener.appeared.contains(taskInfo));
     }
 
@@ -173,8 +171,8 @@
         RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
         TrackingTaskListener mwListener = new TrackingTaskListener();
         TrackingTaskListener pipListener = new TrackingTaskListener();
-        mOrganizer.addListener(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
-        mOrganizer.addListener(pipListener, TASK_LISTENER_TYPE_PIP);
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(pipListener, TASK_LISTENER_TYPE_PIP);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(mwListener.appeared.contains(taskInfo));
         assertTrue(pipListener.appeared.isEmpty());
@@ -185,6 +183,65 @@
         assertTrue(pipListener.appeared.contains(taskInfo));
     }
 
+    @Test
+    public void testAddListenerForTaskId_afterTypeListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.onTaskAppeared(task1, null);
+        assertTrue(mwListener.appeared.contains(task1));
+
+        // Add task 1 specific listener
+        mOrganizer.addListenerForTaskId(task1Listener, 1);
+        assertTrue(mwListener.vanished.contains(task1));
+        assertTrue(task1Listener.appeared.contains(task1));
+    }
+
+    @Test
+    public void testAddListenerForTaskId_beforeTypeListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+        mOrganizer.onTaskAppeared(task1, null);
+        mOrganizer.addListenerForTaskId(task1Listener, 1);
+        assertTrue(task1Listener.appeared.contains(task1));
+
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        assertFalse(mwListener.appeared.contains(task1));
+    }
+
+    @Test
+    public void testGetTaskListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+
+        TrackingTaskListener cookieListener = new TrackingTaskListener();
+        IBinder cookie = new Binder();
+        task1.addLaunchCookie(cookie);
+        mOrganizer.setPendingLaunchCookieListener(cookie, cookieListener);
+
+        // Priority goes to the cookie listener so we would expect the task appear to show up there
+        // instead of the multi-window type listener.
+        mOrganizer.onTaskAppeared(task1, null);
+        assertTrue(cookieListener.appeared.contains(task1));
+        assertFalse(mwListener.appeared.contains(task1));
+
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+
+        boolean gotException = false;
+        try {
+            mOrganizer.addListenerForTaskId(task1Listener, 1);
+        } catch (Exception e) {
+            gotException = true;
+        }
+        // It should not be possible to add a task id listener for a task already mapped to a
+        // listener through cookie.
+        assertTrue(gotException);
+    }
+
     private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index d9e3148..e0ac8e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.content.ComponentName;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -58,15 +57,13 @@
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
-    private ComponentName mTestComponentName1;
-    private ComponentName mTestComponentName2;
+    private PipBoundsState mPipBoundsState;
 
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
-        mTestComponentName1 = new ComponentName(mContext, "component1");
-        mTestComponentName2 = new ComponentName(mContext, "component2");
+        mPipBoundsState = new PipBoundsState();
+        mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
 
         mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
     }
@@ -126,8 +123,8 @@
                 (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
         };
         for (float aspectRatio : aspectRatios) {
-            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+                    EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds matches the given aspect ratio",
@@ -142,8 +139,8 @@
                 MAX_ASPECT_RATIO * 2
         };
         for (float aspectRatio : invalidAspectRatios) {
-            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+                    EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -158,8 +155,8 @@
         final Rect currentBounds = new Rect(0, 0, 0, 100);
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
 
-        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+                currentBounds, EMPTY_MINIMAL_SIZE);
 
         final float actualAspectRatio =
                 destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -182,8 +179,8 @@
         for (int i = 0; i < aspectRatios.length; i++) {
             final float aspectRatio = aspectRatios[i];
             final Size minimalSize = minimalSizes[i];
-            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+                    EMPTY_CURRENT_BOUNDS, minimalSize);
             assertTrue("Destination bounds is no smaller than minimal requirement",
                     (destinationBounds.width() == minimalSize.getWidth()
                             && destinationBounds.height() >= minimalSize.getHeight())
@@ -203,8 +200,8 @@
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
         final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
 
-        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTestComponentName1, aspectRatio, currentBounds, minSize);
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+                currentBounds, minSize);
 
         assertTrue("Destination bounds ignores minimal size",
                 destinationBounds.width() > minSize.getWidth()
@@ -212,28 +209,44 @@
     }
 
     @Test
-    public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() {
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+    public void getDestinationBounds_reentryStateExists_restoreLastSize() {
+        final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        reentryBounds.scale(1.25f);
+        final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
 
-        oldPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+        mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        assertEquals(reentryBounds.width(), destinationBounds.width());
+        assertEquals(reentryBounds.height(), destinationBounds.height());
+    }
 
-        assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition);
+    @Test
+    public void getDestinationBounds_reentryStateExists_restoreLastPosition() {
+        final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        reentryBounds.offset(0, -100);
+        final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
+
+        mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
+
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+        assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds);
     }
 
     @Test
     public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -shelfHeight);
         assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -242,92 +255,30 @@
     @Test
     public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -imeHeight);
         assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition);
     }
 
     @Test
-    public void onSaveReentryBounds_restoreLastPosition() {
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+    public void getDestinationBounds_noReentryState_useDefaultBounds() {
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        oldPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+        mPipBoundsState.clearReentryState();
 
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition);
-    }
-
-    @Test
-    public void onSaveReentryBounds_restoreLastSize() {
-        final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        oldSize.scale(1.25f);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
-
-        final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertEquals(oldSize.width(), newSize.width());
-        assertEquals(oldSize.height(), newSize.height());
-    }
-
-    @Test
-    public void onResetReentryBounds_useDefaultBounds() {
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-        final Rect newBounds = new Rect(defaultBounds);
-        newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName1);
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
     }
 
-    @Test
-    public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-        final Rect newBounds = new Rect(defaultBounds);
-        newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds);
-    }
-
-    @Test
-    public void onSaveReentryBounds_componentMismatch_restoreLastSize() {
-        final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        oldSize.scale(1.25f);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
-        final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertEquals(oldSize.width(), newSize.width());
-        assertEquals(oldSize.height(), newSize.height());
-    }
-
     private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
         final Rect expectedWithMargin = new Rect(expected);
         expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
new file mode 100644
index 0000000..dc9399e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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.pip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link PipBoundsState}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class PipBoundsStateTest extends PipTestCase {
+
+    private static final Rect DEFAULT_BOUNDS = new Rect(0, 0, 10, 10);
+    private static final float DEFAULT_SNAP_FRACTION = 1.0f;
+
+    private PipBoundsState mPipBoundsState;
+    private ComponentName mTestComponentName1;
+    private ComponentName mTestComponentName2;
+
+    @Before
+    public void setUp() {
+        mPipBoundsState = new PipBoundsState();
+        mTestComponentName1 = new ComponentName(mContext, "component1");
+        mTestComponentName2 = new ComponentName(mContext, "component2");
+    }
+
+    @Test
+    public void testSetBounds() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        mPipBoundsState.setBounds(bounds);
+
+        assertEquals(bounds, mPipBoundsState.getBounds());
+    }
+
+    @Test
+    public void testSetReentryState() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        final float snapFraction = 0.5f;
+
+        mPipBoundsState.saveReentryState(bounds, snapFraction);
+
+        final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+        assertEquals(new Size(100, 100), state.getSize());
+        assertEquals(snapFraction, state.getSnapFraction(), 0.01);
+    }
+
+    @Test
+    public void testClearReentryState() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        final float snapFraction = 0.5f;
+
+        mPipBoundsState.saveReentryState(bounds, snapFraction);
+        mPipBoundsState.clearReentryState();
+
+        assertNull(mPipBoundsState.getReentryState());
+    }
+
+    @Test
+    public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION);
+
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+
+        final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+        assertNotNull(state);
+        assertEquals(new Size(DEFAULT_BOUNDS.width(), DEFAULT_BOUNDS.height()), state.getSize());
+        assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01);
+    }
+
+    @Test
+    public void testSetLastPipComponentName_changed_clearReentryState() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION);
+
+        mPipBoundsState.setLastPipComponentName(mTestComponentName2);
+
+        assertNull(mPipBoundsState.getReentryState());
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
similarity index 86%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 46ebbf3..54543d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.pip;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -27,12 +27,6 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.pip.PipBoundsHandler;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTestCase;
-import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
@@ -70,7 +64,7 @@
 
     @Test
     public void instantiatePipTaskOrganizer_addsTaskListener() {
-        verify(mMockShellTaskOrganizer).addListener(any(), anyInt());
+        verify(mMockShellTaskOrganizer).addListenerForType(any(), anyInt());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 4713142..3f60cc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -93,7 +93,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mPipBoundsState = new PipBoundsState();
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
+        mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
         mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
         mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
         mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 8ab7da5..903ca2a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -155,11 +155,12 @@
         android: {
             srcs: [
                 "tests/BackupData_test.cpp",
-		"tests/BackupHelpers_test.cpp",
+                "tests/BackupHelpers_test.cpp",
+                "tests/CursorWindow_test.cpp",
                 "tests/ObbFile_test.cpp",
                 "tests/PosixUtils_test.cpp",
             ],
-            shared_libs: common_test_libs + ["libui"],
+            shared_libs: common_test_libs + ["libbinder", "liblog", "libui"],
         },
         host: {
             static_libs: common_test_libs + ["liblog", "libz"],
@@ -185,9 +186,28 @@
         // Actual benchmarks.
         "tests/AssetManager2_bench.cpp",
         "tests/AttributeResolution_bench.cpp",
+        "tests/CursorWindow_bench.cpp",
         "tests/SparseEntry_bench.cpp",
         "tests/Theme_bench.cpp",
     ],
     shared_libs: common_test_libs,
     data: ["tests/data/**/*.apk"],
 }
+
+cc_library {
+    name: "libandroidfw_fuzzer_lib",
+    defaults: ["libandroidfw_defaults"],
+    host_supported: true,
+    srcs: [
+        "CursorWindow.cpp",
+    ],
+    export_include_dirs: ["include"],
+    target: {
+        android: {
+            shared_libs: common_test_libs + ["libbinder", "liblog"],
+        },
+        host: {
+            static_libs: common_test_libs + ["libbinder", "liblog"],
+        },
+    },
+}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index b9765ea..99dd313 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -39,10 +39,8 @@
 namespace android {
 
 struct FindEntryResult {
-  // A pointer to the resource table entry for this resource.
-  // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
-  // a ResTable_map_entry and processed as a bag/map.
-  ResTable_entry_handle entry;
+  // A pointer to the value of the resource table entry.
+  std::variant<Res_value, const ResTable_map_entry*> entry;
 
   // The configuration for which the resulting entry was defined. This is already swapped to host
   // endianness.
@@ -554,11 +552,9 @@
       if (!overlay_entry) {
         // No id map entry exists for this target resource.
         continue;
-      }
-
-      if (overlay_entry.IsTableEntry()) {
+      } else if (overlay_entry.IsInlineValue()) {
         // The target resource is overlaid by an inline value not represented by a resource.
-        out_entry->entry = overlay_entry.GetTableEntry();
+        out_entry->entry = overlay_entry.GetInlineValue();
         out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
         cookie = id_map.cookie;
         continue;
@@ -580,7 +576,7 @@
       }
 
       cookie = overlay_cookie;
-      out_entry->entry = std::move(overlay_result.entry);
+      out_entry->entry = overlay_result.entry;
       out_entry->config = overlay_result.config;
       out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
       if (resource_resolution_logging_enabled_) {
@@ -761,7 +757,19 @@
     return kInvalidCookie;
   }
 
-  out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
+  const uint16_t entry_size = dtohs(best_entry->size);
+  if (entry_size >= sizeof(ResTable_map_entry) &&
+      (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
+    // The entry represents a bag/map.
+    out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
+  } else {
+    // The entry represents a value.
+    Res_value value;
+    value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
+        reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
+    out_entry->entry = value;
+  }
+
   out_entry->config = *best_config;
   out_entry->type_flags = type_flags;
   out_entry->package_name = &best_package->GetPackageName();
@@ -905,8 +913,8 @@
     return kInvalidCookie;
   }
 
-  const ResTable_entry* table_entry = *entry.entry;
-  if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+  if (result_map_entry != nullptr) {
     if (!may_be_bag) {
       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
       return kInvalidCookie;
@@ -920,11 +928,8 @@
     return cookie;
   }
 
-  const Res_value* device_value = reinterpret_cast<const Res_value*>(
-      reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
-  out_value->copyFrom_dtoh(*device_value);
-
   // Convert the package ID to the runtime assigned package ID.
+  *out_value = std::get<Res_value>(entry.entry);
   entry.dynamic_ref_table->lookupResourceValue(out_value);
 
   *out_selected_config = entry.config;
@@ -1004,19 +1009,15 @@
     return nullptr;
   }
 
-  // Check that the size of the entry header is at least as big as
-  // the desired ResTable_map_entry. Also verify that the entry
-  // was intended to be a map.
-  const ResTable_entry* table_entry = *entry.entry;
-  if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
-      (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+  if (result_map_entry == nullptr) {
     // Not a bag, nothing to do.
     return nullptr;
   }
 
-  const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
-  const ResTable_map* map_entry =
-      reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+  auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
+  auto map_entry = reinterpret_cast<const ResTable_map*>(
+      reinterpret_cast<const uint8_t*>(map) + map->size);
   const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
 
   // Keep track of ids that have already been seen to prevent infinite loops caused by circular
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 71c8e1f..915c0d7 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -14,19 +14,14 @@
  * limitations under the License.
  */
 
-#undef LOG_TAG
 #define LOG_TAG "CursorWindow"
 
 #include <androidfw/CursorWindow.h>
-#include <binder/Parcel.h>
-#include <utils/Log.h>
 
-#include <cutils/ashmem.h>
 #include <sys/mman.h>
 
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
+#include "android-base/logging.h"
+#include "cutils/ashmem.h"
 
 namespace android {
 
@@ -36,11 +31,10 @@
  */
 static constexpr const size_t kInlineSize = 16384;
 
-CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
-                           size_t inflatedSize, bool readOnly) :
-        mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size),
-        mInflatedSize(inflatedSize), mReadOnly(readOnly) {
-    mHeader = static_cast<Header*>(mData);
+static constexpr const size_t kSlotShift = 4;
+static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift;
+
+CursorWindow::CursorWindow() {
 }
 
 CursorWindow::~CursorWindow() {
@@ -52,234 +46,243 @@
     }
 }
 
-status_t CursorWindow::create(const String8& name, size_t inflatedSize,
-                              CursorWindow** outCursorWindow) {
-    *outCursorWindow = nullptr;
+status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
+    *outWindow = nullptr;
 
-    size_t size = std::min(kInlineSize, inflatedSize);
-    void* data = calloc(size, 1);
-    if (!data) return NO_MEMORY;
+    CursorWindow* window = new CursorWindow();
+    if (!window) goto fail;
 
-    CursorWindow* window = new CursorWindow(name, -1, data, size,
-                                            inflatedSize, false /*readOnly*/);
-    status_t result = window->clear();
-    if (!result) {
-        LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
-                "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                window->mHeader->freeOffset,
-                window->mHeader->numRows,
-                window->mHeader->numColumns,
-                window->mSize, window->mData);
-        *outCursorWindow = window;
-        return OK;
-    }
+    window->mName = name;
+    window->mSize = std::min(kInlineSize, inflatedSize);
+    window->mInflatedSize = inflatedSize;
+    window->mData = malloc(window->mSize);
+    if (!window->mData) goto fail;
+    window->mReadOnly = false;
+
+    window->clear();
+    window->updateSlotsData();
+
+    LOG(DEBUG) << "Created: " << window->toString();
+    *outWindow = window;
+    return OK;
+
+fail:
+    LOG(ERROR) << "Failed create";
+fail_silent:
     delete window;
-    return result;
+    return UNKNOWN_ERROR;
 }
 
-status_t CursorWindow::inflate() {
-    // Shortcut when we can't expand any further
-    if (mSize == mInflatedSize) return INVALID_OPERATION;
+status_t CursorWindow::maybeInflate() {
+    int ashmemFd = 0;
+    void* newData = nullptr;
+
+    // Bail early when we can't expand any further
+    if (mReadOnly || mSize == mInflatedSize) {
+        return INVALID_OPERATION;
+    }
 
     String8 ashmemName("CursorWindow: ");
     ashmemName.append(mName);
 
-    status_t result;
-    int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
+    ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
     if (ashmemFd < 0) {
-        result = -errno;
-        ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno);
-    } else {
-        result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
-        if (result < 0) {
-            ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno);
-        } else {
-            void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE,
-                                MAP_SHARED, ashmemFd, 0);
-            if (data == MAP_FAILED) {
-                result = -errno;
-                ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
-            } else {
-                result = ashmem_set_prot_region(ashmemFd, PROT_READ);
-                if (result < 0) {
-                    ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno);
-                } else {
-                    // Move inline contents into new ashmem region
-                    memcpy(data, mData, mSize);
-                    free(mData);
-                    mAshmemFd = ashmemFd;
-                    mData = data;
-                    mHeader = static_cast<Header*>(mData);
-                    mSize = mInflatedSize;
-                    LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, "
-                            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                            mHeader->freeOffset,
-                            mHeader->numRows,
-                            mHeader->numColumns,
-                            mSize, mData);
-                    return OK;
-                }
-            }
-            ::munmap(data, mInflatedSize);
-        }
-        ::close(ashmemFd);
+        PLOG(ERROR) << "Failed ashmem_create_region";
+        goto fail_silent;
     }
-    return result;
+
+    if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) {
+        PLOG(ERROR) << "Failed ashmem_set_prot_region";
+        goto fail_silent;
+    }
+
+    newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+    if (newData == MAP_FAILED) {
+        PLOG(ERROR) << "Failed mmap";
+        goto fail_silent;
+    }
+
+    if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
+        PLOG(ERROR) << "Failed ashmem_set_prot_region";
+        goto fail_silent;
+    }
+
+    {
+        // Migrate existing contents into new ashmem region
+        uint32_t slotsSize = mSize - mSlotsOffset;
+        uint32_t newSlotsOffset = mInflatedSize - slotsSize;
+        memcpy(static_cast<uint8_t*>(newData),
+                static_cast<uint8_t*>(mData), mAllocOffset);
+        memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
+                static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
+
+        free(mData);
+        mAshmemFd = ashmemFd;
+        mData = newData;
+        mSize = mInflatedSize;
+        mSlotsOffset = newSlotsOffset;
+
+        updateSlotsData();
+    }
+
+    LOG(DEBUG) << "Inflated: " << this->toString();
+    return OK;
+
+fail:
+    LOG(ERROR) << "Failed maybeInflate";
+fail_silent:
+    ::munmap(newData, mInflatedSize);
+    ::close(ashmemFd);
+    return UNKNOWN_ERROR;
 }
 
-status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
-    *outCursorWindow = nullptr;
+status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
+    *outWindow = nullptr;
 
-    String8 name;
-    status_t result = parcel->readString8(&name);
-    if (result) return result;
+    CursorWindow* window = new CursorWindow();
+    if (!window) goto fail;
+
+    if (parcel->readString8(&window->mName)) goto fail;
+    if (parcel->readUint32(&window->mNumRows)) goto fail;
+    if (parcel->readUint32(&window->mNumColumns)) goto fail;
+    if (parcel->readUint32(&window->mSize)) goto fail;
+
+    if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) {
+        LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows
+                << " rows and " << window->mNumColumns << " columns";
+        goto fail_silent;
+    }
 
     bool isAshmem;
-    result = parcel->readBool(&isAshmem);
-    if (result) return result;
-
+    if (parcel->readBool(&isAshmem)) goto fail;
     if (isAshmem) {
-        return createFromParcelAshmem(parcel, name, outCursorWindow);
-    } else {
-        return createFromParcelInline(parcel, name, outCursorWindow);
-    }
-}
-
-status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name,
-                                              CursorWindow** outCursorWindow) {
-    status_t result;
-    int actualSize;
-    int ashmemFd = parcel->readFileDescriptor();
-    if (ashmemFd == int(BAD_TYPE)) {
-        result = BAD_TYPE;
-        ALOGE("CursorWindow: readFileDescriptor() failed");
-    } else {
-        ssize_t size = ashmem_get_size_region(ashmemFd);
-        if (size < 0) {
-            result = UNKNOWN_ERROR;
-            ALOGE("CursorWindow: ashmem_get_size_region() failed: errno=%d.", errno);
-        } else {
-            int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
-            if (dupAshmemFd < 0) {
-                result = -errno;
-                ALOGE("CursorWindow: fcntl() failed: errno=%d.", errno);
-            } else {
-                // the size of the ashmem descriptor can be modified between ashmem_get_size_region
-                // call and mmap, so we'll check again immediately after memory is mapped
-                void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
-                if (data == MAP_FAILED) {
-                    result = -errno;
-                    ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
-                } else if ((actualSize = ashmem_get_size_region(dupAshmemFd)) != size) {
-                    ::munmap(data, size);
-                    result = BAD_VALUE;
-                    ALOGE("CursorWindow: ashmem_get_size_region() returned %d, expected %d"
-                            " errno=%d",
-                            actualSize, (int) size, errno);
-                } else {
-                    CursorWindow* window = new CursorWindow(name, dupAshmemFd,
-                            data, size, size, true /*readOnly*/);
-                    LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, "
-                            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                            window->mHeader->freeOffset,
-                            window->mHeader->numRows,
-                            window->mHeader->numColumns,
-                            window->mSize, window->mData);
-                    *outCursorWindow = window;
-                    return OK;
-                }
-                ::close(dupAshmemFd);
-            }
+        window->mAshmemFd = parcel->readFileDescriptor();
+        if (window->mAshmemFd < 0) {
+            LOG(ERROR) << "Failed readFileDescriptor";
+            goto fail_silent;
         }
+
+        window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0);
+        if (window->mAshmemFd < 0) {
+            PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
+            goto fail_silent;
+        }
+
+        window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
+        if (window->mData == MAP_FAILED) {
+            PLOG(ERROR) << "Failed mmap";
+            goto fail_silent;
+        }
+    } else {
+        window->mAshmemFd = -1;
+
+        if (window->mSize > kInlineSize) {
+            LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window";
+            goto fail_silent;
+        }
+
+        window->mData = malloc(window->mSize);
+        if (!window->mData) goto fail;
+
+        if (parcel->read(window->mData, window->mSize)) goto fail;
     }
-    *outCursorWindow = NULL;
-    return result;
-}
 
-status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name,
-                                              CursorWindow** outCursorWindow) {
-    uint32_t sentSize;
-    status_t result = parcel->readUint32(&sentSize);
-    if (result) return result;
-    if (sentSize > kInlineSize) return NO_MEMORY;
+    // We just came from a remote source, so we're read-only
+    // and we can't inflate ourselves
+    window->mInflatedSize = window->mSize;
+    window->mReadOnly = true;
 
-    void* data = calloc(sentSize, 1);
-    if (!data) return NO_MEMORY;
+    window->updateSlotsData();
 
-    result = parcel->read(data, sentSize);
-    if (result) return result;
-
-    CursorWindow* window = new CursorWindow(name, -1, data, sentSize,
-                                            sentSize, true /*readOnly*/);
-    LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, "
-            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-            window->mHeader->freeOffset,
-            window->mHeader->numRows,
-            window->mHeader->numColumns,
-            window->mSize, window->mData);
-    *outCursorWindow = window;
+    LOG(DEBUG) << "Created from parcel: " << window->toString();
+    *outWindow = window;
     return OK;
+
+fail:
+    LOG(ERROR) << "Failed createFromParcel";
+fail_silent:
+    delete window;
+    return UNKNOWN_ERROR;
 }
 
 status_t CursorWindow::writeToParcel(Parcel* parcel) {
-        LOG_WINDOW("Writing CursorWindow: freeOffset=%d, "
-                "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                mHeader->freeOffset,
-                mHeader->numRows,
-                mHeader->numColumns,
-                mSize, mData);
+    LOG(DEBUG) << "Writing to parcel: " << this->toString();
 
-    status_t result = parcel->writeString8(mName);
-    if (result) return result;
-
+    if (parcel->writeString8(mName)) goto fail;
+    if (parcel->writeUint32(mNumRows)) goto fail;
+    if (parcel->writeUint32(mNumColumns)) goto fail;
     if (mAshmemFd != -1) {
-        result = parcel->writeBool(true);
-        if (result) return result;
-        return writeToParcelAshmem(parcel);
+        if (parcel->writeUint32(mSize)) goto fail;
+        if (parcel->writeBool(true)) goto fail;
+        if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
     } else {
-        result = parcel->writeBool(false);
-        if (result) return result;
-        return writeToParcelInline(parcel);
+        // Since we know we're going to be read-only on the remote side,
+        // we can compact ourselves on the wire, with just enough padding
+        // to ensure our slots stay aligned
+        size_t slotsSize = mSize - mSlotsOffset;
+        size_t compactedSize = mAllocOffset + slotsSize;
+        compactedSize = (compactedSize + 3) & ~3;
+        if (parcel->writeUint32(compactedSize)) goto fail;
+        if (parcel->writeBool(false)) goto fail;
+        void* dest = parcel->writeInplace(compactedSize);
+        if (!dest) goto fail;
+        memcpy(static_cast<uint8_t*>(dest),
+                static_cast<uint8_t*>(mData), mAllocOffset);
+        memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
+                static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
     }
-}
+    return OK;
 
-status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) {
-    return parcel->writeDupFileDescriptor(mAshmemFd);
-}
-
-status_t CursorWindow::writeToParcelInline(Parcel* parcel) {
-    status_t result = parcel->writeUint32(mHeader->freeOffset);
-    if (result) return result;
-
-    return parcel->write(mData, mHeader->freeOffset);
+fail:
+    LOG(ERROR) << "Failed writeToParcel";
+fail_silent:
+    return UNKNOWN_ERROR;
 }
 
 status_t CursorWindow::clear() {
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
-    mHeader->firstChunkOffset = sizeof(Header);
-    mHeader->numRows = 0;
-    mHeader->numColumns = 0;
-
-    RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
-    firstChunk->nextChunkOffset = 0;
+    mAllocOffset = 0;
+    mSlotsOffset = mSize;
+    mNumRows = 0;
+    mNumColumns = 0;
     return OK;
 }
 
+void CursorWindow::updateSlotsData() {
+    mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
+    mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
+}
+
+void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
+    if (offset > mSize) {
+        LOG(ERROR) << "Offset " << offset
+                << " out of bounds, max value " << mSize;
+        return nullptr;
+    }
+    if (offset + bufferSize > mSize) {
+        LOG(ERROR) << "End offset " << (offset + bufferSize)
+                << " out of bounds, max value " << mSize;
+        return nullptr;
+    }
+    return static_cast<uint8_t*>(mData) + offset;
+}
+
+uint32_t CursorWindow::offsetFromPtr(void* ptr) {
+    return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
+}
+
 status_t CursorWindow::setNumColumns(uint32_t numColumns) {
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    uint32_t cur = mHeader->numColumns;
-    if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
-        ALOGE("Trying to go from %d columns to %d", cur, numColumns);
+    uint32_t cur = mNumColumns;
+    if ((cur > 0 || mNumRows > 0) && cur != numColumns) {
+        LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns;
         return INVALID_OPERATION;
     }
-    mHeader->numColumns = numColumns;
+    mNumColumns = numColumns;
     return OK;
 }
 
@@ -287,30 +290,19 @@
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    // Fill in the row slot
-    RowSlot* rowSlot = allocRowSlot();
-    if (rowSlot == NULL) {
-        return NO_MEMORY;
+    size_t size = mNumColumns * kSlotSizeBytes;
+    off_t newOffset = mSlotsOffset - size;
+    if (newOffset < mAllocOffset) {
+        maybeInflate();
+        newOffset = mSlotsOffset - size;
+        if (newOffset < mAllocOffset) {
+            return NO_MEMORY;
+        }
     }
-    uint32_t rowSlotOffset = offsetFromPtr(rowSlot);
-
-    // Allocate the slots for the field directory
-    size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
-    uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
-    if (!fieldDirOffset) {
-        mHeader->numRows--;
-        LOG_WINDOW("The row failed, so back out the new row accounting "
-                "from allocRowSlot %d", mHeader->numRows);
-        return NO_MEMORY;
-    }
-    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
-    memset(fieldDir, 0, fieldDirSize);
-
-    LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n",
-            mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset);
-    rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset));
-    rowSlot->offset = fieldDirOffset;
+    memset(offsetToPtr(newOffset), 0, size);
+    mSlotsOffset = newOffset;
+    updateSlotsData();
+    mNumRows++;
     return OK;
 }
 
@@ -318,90 +310,48 @@
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    if (mHeader->numRows > 0) {
-        mHeader->numRows--;
+    size_t size = mNumColumns * kSlotSizeBytes;
+    off_t newOffset = mSlotsOffset + size;
+    if (newOffset > mSize) {
+        return NO_MEMORY;
     }
+    mSlotsOffset = newOffset;
+    updateSlotsData();
+    mNumRows--;
     return OK;
 }
 
-uint32_t CursorWindow::alloc(size_t size, bool aligned) {
-    uint32_t padding;
-    if (aligned) {
-        // 4 byte alignment
-        padding = (~mHeader->freeOffset + 1) & 3;
-    } else {
-        padding = 0;
+status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
+    if (mReadOnly) {
+        return INVALID_OPERATION;
     }
-
-    uint32_t offset = mHeader->freeOffset + padding;
-    uint32_t nextFreeOffset = offset + size;
-    if (nextFreeOffset > mSize) {
-        // Try inflating to ashmem before finally giving up
-        inflate();
-        if (nextFreeOffset > mSize) {
-            ALOGW("Window is full: requested allocation %zu bytes, "
-                    "free space %zu bytes, window size %zu bytes",
-                    size, freeSpace(), mSize);
-            return 0;
+    size_t alignedSize = (size + 3) & ~3;
+    off_t newOffset = mAllocOffset + alignedSize;
+    if (newOffset > mSlotsOffset) {
+        maybeInflate();
+        newOffset = mAllocOffset + alignedSize;
+        if (newOffset > mSlotsOffset) {
+            return NO_MEMORY;
         }
     }
-
-    mHeader->freeOffset = nextFreeOffset;
-    return offset;
-}
-
-CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
-    uint32_t chunkPos = row;
-    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
-            offsetToPtr(mHeader->firstChunkOffset));
-    while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
-    }
-    return &chunk->slots[chunkPos];
-}
-
-CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
-    uint32_t chunkPos = mHeader->numRows;
-    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
-            offsetToPtr(mHeader->firstChunkOffset));
-    while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
-    }
-    if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
-        if (!chunk->nextChunkOffset) {
-            uint32_t chunkOffset = offsetFromPtr(chunk);
-            uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/);
-            chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset));
-            chunk->nextChunkOffset = newChunk;
-            if (!chunk->nextChunkOffset) {
-                return NULL;
-            }
-        }
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunk->nextChunkOffset = 0;
-        chunkPos = 0;
-    }
-    mHeader->numRows += 1;
-    return &chunk->slots[chunkPos];
+    *outOffset = mAllocOffset;
+    mAllocOffset = newOffset;
+    return OK;
 }
 
 CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
-    if (row >= mHeader->numRows || column >= mHeader->numColumns) {
-        ALOGE("Failed to read row %d, column %d from a CursorWindow which "
-                "has %d rows, %d columns.",
-                row, column, mHeader->numRows, mHeader->numColumns);
-        return NULL;
+    // This is carefully tuned to use as few cycles as
+    // possible, since this is an extremely hot code path;
+    // see CursorWindow_bench.cpp for more details
+    void *result = static_cast<uint8_t*>(mSlotsStart)
+            - (((row * mNumColumns) + column) << kSlotShift);
+    if (result < mSlotsEnd || column >= mNumColumns) {
+        LOG(ERROR) << "Failed to read row " << row << ", column " << column
+                << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
+        return nullptr;
+    } else {
+        return static_cast<FieldSlot*>(result);
     }
-    RowSlot* rowSlot = getRowSlot(row);
-    if (!rowSlot) {
-        ALOGE("Failed to find rowSlot for row %d.", row);
-        return NULL;
-    }
-    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
-    return &fieldDir[column];
 }
 
 status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
@@ -423,16 +373,15 @@
     if (!fieldSlot) {
         return BAD_VALUE;
     }
-    uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot);
 
-    uint32_t offset = alloc(size);
-    if (!offset) {
+    uint32_t offset;
+    if (alloc(size, &offset)) {
         return NO_MEMORY;
     }
 
     memcpy(offsetToPtr(offset), value, size);
 
-    fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset));
+    fieldSlot = getFieldSlot(row, column);
     fieldSlot->type = type;
     fieldSlot->data.buffer.offset = offset;
     fieldSlot->data.buffer.size = size;
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 5f231ff..4e03ce5 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -36,16 +36,12 @@
 
 namespace android {
 
-static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
-  return dtohl(e1.target_id) < target_id;
-}
-
-static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
-  return dtohl(e1.overlay_id) < overlay_id;
+uint32_t round_to_4_bytes(uint32_t size) {
+  return size + (4U - (size % 4U)) % 4U;
 }
 
 size_t Idmap_header::Size() const {
-  return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
+  return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size));
 }
 
 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
@@ -88,7 +84,10 @@
 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
   const Idmap_overlay_entry* first_entry = entries_;
   const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
+  auto entry = std::lower_bound(first_entry, end_entry, *resId,
+                                [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
+    return dtohl(e1.overlay_id) < overlay_id;
+  });
 
   if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
     // A mapping for the target resource id could not be found.
@@ -96,7 +95,7 @@
   }
 
   *resId = (0x00FFFFFFU & dtohl(entry->target_id))
-      | (((uint32_t) target_assigned_package_id_) << 24);
+      | (((uint32_t) target_assigned_package_id_) << 24U);
   return NO_ERROR;
 }
 
@@ -106,62 +105,58 @@
 
 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
                          const Idmap_target_entry* entries,
+                         const Idmap_target_entry_inline* inline_entries,
                          uint8_t target_assigned_package_id,
                          const OverlayDynamicRefTable* overlay_ref_table)
     : data_header_(data_header),
       entries_(entries),
+      inline_entries_(inline_entries),
       target_assigned_package_id_(target_assigned_package_id),
-      overlay_ref_table_(overlay_ref_table) { };
+      overlay_ref_table_(overlay_ref_table) { }
 
 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
-  if ((target_res_id >> 24) != target_assigned_package_id_) {
+  if ((target_res_id >> 24U) != target_assigned_package_id_) {
     // The resource id must have the same package id as the target package.
     return {};
   }
 
   // The resource ids encoded within the idmap are build-time resource ids.
   target_res_id = (0x00FFFFFFU & target_res_id)
-      | (((uint32_t) data_header_->target_package_id) << 24);
+      | (((uint32_t) data_header_->target_package_id) << 24U);
 
-  const Idmap_target_entry* first_entry = entries_;
-  const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+  // Check if the target resource is mapped to an overlay resource.
+  auto first_entry = entries_;
+  auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
+  auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
+                                [](const Idmap_target_entry &e, const uint32_t target_id) {
+    return dtohl(e.target_id) < target_id;
+  });
 
-  if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
-    // A mapping for the target resource id could not be found.
-    return {};
-  }
-
-  // A reference should be treated as an alias of the resource. Instead of returning the table
-  // entry, return the alias resource id to look up. The alias resource might not reside within the
-  // overlay package, so the resource id must be fixed with the dynamic reference table of the
-  // overlay before returning.
-  if (entry->type == Res_value::TYPE_REFERENCE
-      || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
-    uint32_t overlay_resource_id = dtohl(entry->value);
-
+  if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
+    uint32_t overlay_resource_id = dtohl(entry->overlay_id);
     // Lookup the resource without rewriting the overlay resource id back to the target resource id
     // being looked up.
     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
     return Result(overlay_resource_id);
   }
 
-  // Copy the type and value into the ResTable_entry structure needed by asset manager.
-  uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
-  auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
-  memset(table_entry, 0, malloc_size);
-  table_entry->size = htods(sizeof(ResTable_entry));
+  // Check if the target resources is mapped to an inline table entry.
+  auto first_inline_entry = inline_entries_;
+  auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
+  auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
+                                       [](const Idmap_target_entry_inline &e,
+                                          const uint32_t target_id) {
+    return dtohl(e.target_id) < target_id;
+  });
 
-  auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
-      + sizeof(ResTable_entry));
-  table_value->dataType = entry->type;
-  table_value->data = entry->value;
-
-  return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
+  if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
+    return Result(inline_entry->value);
+  }
+  return {};
 }
 
 static bool is_word_aligned(const void* data) {
-  return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
+  return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U;
 }
 
 static bool IsValidIdmapHeader(const StringPiece& data) {
@@ -175,7 +170,7 @@
     return false;
   }
 
-  const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
+  auto header = reinterpret_cast<const Idmap_header*>(data.data());
   if (dtohl(header->magic) != kIdmapMagic) {
     LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
                                dtohl(header->magic), kIdmapMagic);
@@ -198,11 +193,13 @@
                          const Idmap_header* header,
                          const Idmap_data_header* data_header,
                          const Idmap_target_entry* target_entries,
+                         const Idmap_target_entry_inline* target_inline_entries,
                          const Idmap_overlay_entry* overlay_entries,
                          ResStringPool* string_pool)
      : header_(header),
        data_header_(data_header),
        target_entries_(target_entries),
+       target_inline_entries_(target_inline_entries),
        overlay_entries_(overlay_entries),
        string_pool_(string_pool),
        idmap_path_(std::move(idmap_path)),
@@ -233,7 +230,7 @@
   data_ptr += sizeof(*data_header);
   data_size -= sizeof(*data_header);
 
-  // Make sure there is enough space for the target entries declared in the header.
+  // Make sure there is enough space for the target entries declared in the header
   const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
   if (data_size / sizeof(Idmap_target_entry) <
       static_cast<size_t>(dtohl(data_header->target_entry_count))) {
@@ -248,6 +245,21 @@
   data_ptr += target_entry_size_bytes;
   data_size -= target_entry_size_bytes;
 
+  // Make sure there is enough space for the target entries declared in the header.
+  const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr);
+  if (data_size / sizeof(Idmap_target_entry_inline) <
+      static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) {
+    LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)",
+                               (int)dtohl(data_header->target_inline_entry_count));
+    return {};
+  }
+
+  // Advance the data pointer past the target entries.
+  const size_t target_inline_entry_size_bytes =
+      (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline));
+  data_ptr += target_inline_entry_size_bytes;
+  data_size -= target_inline_entry_size_bytes;
+
   // Make sure there is enough space for the overlay entries declared in the header.
   const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
   if (data_size / sizeof(Idmap_overlay_entry) <
@@ -257,22 +269,26 @@
     return {};
   }
 
-  // Advance the data pointer past the target entries.
+  // Advance the data pointer past the overlay entries.
   const size_t overlay_entry_size_bytes =
       (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
   data_ptr += overlay_entry_size_bytes;
   data_size -= overlay_entry_size_bytes;
 
   // Read the idmap string pool that holds the value of inline string entries.
-  if (data_size < dtohl(data_header->string_pool_length)) {
+  uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr));
+  data_ptr += sizeof(uint32_t);
+  data_size -= sizeof(uint32_t);
+
+  if (data_size < string_pool_size) {
     LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
-                               (int)dtohl(data_header->string_pool_length));
+                               (int)string_pool_size);
     return {};
   }
 
   auto idmap_string_pool = util::make_unique<ResStringPool>();
-  if (dtohl(data_header->string_pool_length) > 0) {
-    status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+  if (string_pool_size > 0) {
+    status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
     if (err != NO_ERROR) {
       LOG(ERROR) << "idmap string pool corrupt.";
       return {};
@@ -280,9 +296,10 @@
   }
 
   // Can't use make_unique because LoadedIdmap constructor is private.
-  std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
+  auto loaded_idmap = std::unique_ptr<LoadedIdmap>(
       new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
-                      data_header, target_entries, overlay_entries, idmap_string_pool.release()));
+                      data_header, target_entries, target_inline_entries, overlay_entries,
+                      idmap_string_pool.release()));
 
   return std::move(loaded_idmap);
 }
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
new file mode 100644
index 0000000..2dac47b
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -0,0 +1,31 @@
+cc_fuzz {
+    name: "cursorwindow_fuzzer",
+    srcs: [
+        "cursorwindow_fuzzer.cpp",
+    ],
+    host_supported: true,
+    corpus: ["corpus/*"],
+    static_libs: ["libgmock"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw_fuzzer_lib",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw_fuzzer_lib",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+    },
+}
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
new file mode 100644
index 0000000..c7e22dd
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
Binary files differ
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
new file mode 100644
index 0000000..8dce212
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <memory>
+
+#include "android-base/logging.h"
+#include "androidfw/CursorWindow.h"
+#include "binder/Parcel.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::CursorWindow;
+using android::Parcel;
+
+extern "C" int LLVMFuzzerInitialize(int *, char ***) {
+    setenv("ANDROID_LOG_TAGS", "*:s", 1);
+    android::base::InitLogging(nullptr, &android::base::StderrLogger);
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    Parcel p;
+    p.setData(data, size);
+
+    CursorWindow* w = nullptr;
+    if (!CursorWindow::createFromParcel(&p, &w)) {
+        LOG(WARNING) << "Valid cursor with " << w->getNumRows() << " rows, "
+                << w->getNumColumns() << " cols";
+
+        // Try obtaining heap allocations for most items; we trim the
+        // search space to speed things up
+        auto rows = std::min(w->getNumRows(), static_cast<uint32_t>(128));
+        auto cols = std::min(w->getNumColumns(), static_cast<uint32_t>(128));
+        for (auto row = 0; row < rows; row++) {
+            for (auto col = 0; col < cols; col++) {
+                auto field = w->getFieldSlot(row, col);
+                if (!field) continue;
+                switch (w->getFieldSlotType(field)) {
+                case CursorWindow::FIELD_TYPE_STRING: {
+                    size_t size;
+                    w->getFieldSlotValueString(field, &size);
+                    break;
+                }
+                case CursorWindow::FIELD_TYPE_BLOB: {
+                    size_t size;
+                    w->getFieldSlotValueBlob(field, &size);
+                    break;
+                }
+                }
+            }
+        }
+
+        // Finally, try obtaining the furthest valid field
+        if (rows > 0 && cols > 0) {
+            w->getFieldSlot(w->getNumRows() - 1, w->getNumColumns() - 1);
+        }
+    }
+    delete w;
+
+    return 0;
+}
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 0bee609..6e55a9a 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -20,38 +20,36 @@
 #include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <string>
 
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/String8.h>
+#include "android-base/stringprintf.h"
+#include "binder/Parcel.h"
+#include "utils/String8.h"
 
-#if LOG_NDEBUG
-
-#define IF_LOG_WINDOW() if (false)
 #define LOG_WINDOW(...)
 
-#else
-
-#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
-
-#endif
-
 namespace android {
 
 /**
- * This class stores a set of rows from a database in a buffer. The begining of the
- * window has first chunk of RowSlots, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
- * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * FieldSlot per column, which has the size, offset, and type of the data for that field.
- * Note that the data types come from sqlite3.h.
+ * This class stores a set of rows from a database in a buffer. Internally
+ * data is structured as a "heap" of string/blob allocations at the bottom
+ * of the memory region, and a "stack" of FieldSlot allocations at the top
+ * of the memory region. Here's an example visual representation:
+ *
+ *   +----------------------------------------------------------------+
+ *   |heap\0of\0strings\0                                 222211110000| ...
+ *   +-------------------+--------------------------------+-------+---+
+ *    ^                  ^                                ^       ^   ^     ^
+ *    |                  |                                |       |   |     |
+ *    |                  +- mAllocOffset    mSlotsOffset -+       |   |     |
+ *    +- mData                                       mSlotsStart -+   |     |
+ *                                                             mSize -+     |
+ *                                                           mInflatedSize -+
  *
  * Strings are stored in UTF-8.
  */
 class CursorWindow {
-    CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
-                 size_t inflatedSize, bool readOnly);
+    CursorWindow();
 
 public:
     /* Field types. */
@@ -88,9 +86,9 @@
 
     inline String8 name() { return mName; }
     inline size_t size() { return mSize; }
-    inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
-    inline uint32_t getNumRows() { return mHeader->numRows; }
-    inline uint32_t getNumColumns() { return mHeader->numColumns; }
+    inline size_t freeSpace() { return mSlotsOffset - mAllocOffset; }
+    inline uint32_t getNumRows() { return mNumRows; }
+    inline uint32_t getNumColumns() { return mNumColumns; }
 
     status_t clear();
     status_t setNumColumns(uint32_t numColumns);
@@ -138,75 +136,57 @@
         return offsetToPtr(fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size);
     }
 
+    inline std::string toString() const {
+        return android::base::StringPrintf("CursorWindow{name=%s, fd=%d, size=%d, inflatedSize=%d, "
+                "allocOffset=%d, slotsOffset=%d, numRows=%d, numColumns=%d}", mName.c_str(),
+                mAshmemFd, mSize, mInflatedSize, mAllocOffset, mSlotsOffset, mNumRows, mNumColumns);
+    }
+
 private:
-    static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
-
-    struct Header {
-        // Offset of the lowest unused byte in the window.
-        uint32_t freeOffset;
-
-        // Offset of the first row slot chunk.
-        uint32_t firstChunkOffset;
-
-        uint32_t numRows;
-        uint32_t numColumns;
-    };
-
-    struct RowSlot {
-        uint32_t offset;
-    };
-
-    struct RowSlotChunk {
-        RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
-        uint32_t nextChunkOffset;
-    };
-
     String8 mName;
-    int mAshmemFd;
-    void* mData;
-    size_t mSize;
-    size_t mInflatedSize;
-    bool mReadOnly;
-    Header* mHeader;
+    int mAshmemFd = -1;
+    void* mData = nullptr;
+    /**
+     * Pointer to the first FieldSlot, used to optimize the extremely
+     * hot code path of getFieldSlot().
+     */
+    void* mSlotsStart = nullptr;
+    void* mSlotsEnd = nullptr;
+    uint32_t mSize = 0;
+    /**
+     * When a window starts as lightweight inline allocation, this value
+     * holds the "full" size to be created after ashmem inflation.
+     */
+    uint32_t mInflatedSize = 0;
+    /**
+     * Offset to the top of the "heap" of string/blob allocations. By
+     * storing these allocations at the bottom of our memory region we
+     * avoid having to rewrite offsets when inflating.
+     */
+    uint32_t mAllocOffset = 0;
+    /**
+     * Offset to the bottom of the "stack" of FieldSlot allocations.
+     */
+    uint32_t mSlotsOffset = 0;
+    uint32_t mNumRows = 0;
+    uint32_t mNumColumns = 0;
+    bool mReadOnly = false;
 
-    inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
-        if (offset > mSize) {
-            ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize);
-            return NULL;
-        }
-        if (offset + bufferSize > mSize) {
-            ALOGE("End offset %" PRIu32 " out of bounds, max value %zu",
-                    offset + bufferSize, mSize);
-            return NULL;
-        }
-        return static_cast<uint8_t*>(mData) + offset;
-    }
+    void updateSlotsData();
 
-    inline uint32_t offsetFromPtr(void* ptr) {
-        return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
-    }
-
-    static status_t createFromParcelAshmem(Parcel*, String8&, CursorWindow**);
-    static status_t createFromParcelInline(Parcel*, String8&, CursorWindow**);
-
-    status_t writeToParcelAshmem(Parcel*);
-    status_t writeToParcelInline(Parcel*);
+    void* offsetToPtr(uint32_t offset, uint32_t bufferSize);
+    uint32_t offsetFromPtr(void* ptr);
 
     /**
      * By default windows are lightweight inline allocations; this method
      * inflates the window into a larger ashmem region.
      */
-    status_t inflate();
+    status_t maybeInflate();
 
     /**
-     * Allocate a portion of the window. Returns the offset
-     * of the allocation, or 0 if there isn't enough space.
-     * If aligned is true, the allocation gets 4 byte alignment.
+     * Allocate a portion of the window.
      */
-    uint32_t alloc(size_t size, bool aligned = false);
-
-    RowSlot* getRowSlot(uint32_t row);
-    RowSlot* allocRowSlot();
+    status_t alloc(size_t size, uint32_t* outOffset);
 
     status_t putBlobOrString(uint32_t row, uint32_t column,
             const void* value, size_t size, int32_t type);
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ecc1ce6..ab0f47f 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -77,40 +77,40 @@
 // A mapping of target resource ids to a values or resource ids that should overlay the target.
 class IdmapResMap {
  public:
-  // Represents the result of a idmap lookup. The result can be one of three possibillities:
+  // Represents the result of a idmap lookup. The result can be one of three possibilities:
   // 1) The result is a resource id which represents the overlay resource that should act as an
   //    alias of the target resource.
   // 2) The result is a table entry which overlays the type and value of the target resource.
   // 3) The result is neither and the target resource is not overlaid.
   class Result {
    public:
-    Result() : data_(nullptr) {};
+    Result() = default;
     explicit Result(uint32_t value) : data_(value) {};
-    explicit Result(ResTable_entry_handle&& value) : data_(value) { };
+    explicit Result(const Res_value& value) : data_(value) { };
 
     // Returns `true` if the resource is overlaid.
-    inline explicit operator bool() const {
-      return !std::get_if<nullptr_t>(&data_);
+    explicit operator bool() const {
+      return std::get_if<std::monostate>(&data_) == nullptr;
     }
 
-    inline bool IsResourceId() const {
-      return std::get_if<uint32_t>(&data_);
+    bool IsResourceId() const {
+      return std::get_if<uint32_t>(&data_) != nullptr;
     }
 
-    inline uint32_t GetResourceId() const {
-      return *std::get_if<uint32_t>(&data_);
+    uint32_t GetResourceId() const {
+      return std::get<uint32_t>(data_);
     }
 
-    inline bool IsTableEntry() const {
-      return std::get_if<ResTable_entry_handle>(&data_);
+    bool IsInlineValue() const {
+      return std::get_if<Res_value>(&data_) != nullptr;
     }
 
-    inline const ResTable_entry_handle& GetTableEntry() const {
-      return *std::get_if<ResTable_entry_handle>(&data_);
+    const Res_value& GetInlineValue() const {
+      return std::get<Res_value>(data_);
     }
 
    private:
-      std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
+      std::variant<std::monostate, uint32_t, Res_value> data_;
   };
 
   // Looks up the value that overlays the target resource id.
@@ -123,11 +123,13 @@
  private:
   explicit IdmapResMap(const Idmap_data_header* data_header,
                        const Idmap_target_entry* entries,
+                       const Idmap_target_entry_inline* inline_entries,
                        uint8_t target_assigned_package_id,
                        const OverlayDynamicRefTable* overlay_ref_table);
 
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* entries_;
+  const Idmap_target_entry_inline* inline_entries_;
   const uint8_t target_assigned_package_id_;
   const OverlayDynamicRefTable* overlay_ref_table_;
 
@@ -163,8 +165,8 @@
   // Returns a mapping from target resource ids to overlay values.
   inline const IdmapResMap GetTargetResourcesMap(
       uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
-    return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
-                       overlay_ref_table);
+    return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
+                       target_assigned_package_id, overlay_ref_table);
   }
 
   // Returns a dynamic reference table for a loaded overlay package.
@@ -184,6 +186,7 @@
   const Idmap_header* header_;
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* target_entries_;
+  const Idmap_target_entry_inline* target_inline_entries_;
   const Idmap_overlay_entry* overlay_entries_;
   const std::unique_ptr<ResStringPool> string_pool_;
 
@@ -200,6 +203,7 @@
                        const Idmap_header* header,
                        const Idmap_data_header* data_header,
                        const Idmap_target_entry* target_entries,
+                       const Idmap_target_entry_inline* target_inline_entries,
                        const Idmap_overlay_entry* overlay_entries,
                        ResStringPool* string_pool);
 
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e10a7f3..04ba78b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,7 +41,7 @@
 namespace android {
 
 constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u;
 
 /**
  * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1476,7 +1476,7 @@
         // If set, this is a weak resource and may be overriden by strong
         // resources of the same name/type. This is only useful during
         // linking with other resource tables.
-        FLAG_WEAK = 0x0004
+        FLAG_WEAK = 0x0004,
     };
     uint16_t flags;
     
@@ -1586,50 +1586,6 @@
     Res_value value;
 };
 
-
-// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
-// holds a ResTable_entry which is tied to the lifetime of the handle.
-class ResTable_entry_handle {
- public:
-    ResTable_entry_handle() = default;
-
-    ResTable_entry_handle(const ResTable_entry_handle& handle) {
-      entry_ = handle.entry_;
-    }
-
-    ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
-      entry_ = handle.entry_;
-    }
-
-    inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
-      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter));
-    }
-
-    inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry)  {
-      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
-    }
-
-    inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
-      entry_ = handle.entry_;
-      return *this;
-    }
-
-    inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
-      entry_ = handle.entry_;
-      return *this;
-    }
-
-    inline const ResTable_entry* operator*() & {
-      return entry_.get();
-    }
-
- private:
-    explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
-        : entry_(std::move(entry)) { }
-
-    std::shared_ptr<const ResTable_entry> entry_;
-};
-
 /**
  * A package-id to package name mapping for any shared libraries used
  * in this resource table. The package-id's encoded in this resource
@@ -1740,7 +1696,6 @@
   return first;
 }
 
-#pragma pack(push, 1)
 struct Idmap_header {
   // Always 0x504D4449 ('IDMP')
   uint32_t magic;
@@ -1751,7 +1706,7 @@
   uint32_t overlay_crc32;
 
   uint32_t fulfilled_policies;
-  uint8_t enforce_overlayable;
+  uint32_t enforce_overlayable;
 
   uint8_t target_path[256];
   uint8_t overlay_path[256];
@@ -1765,23 +1720,31 @@
 struct Idmap_data_header {
   uint8_t target_package_id;
   uint8_t overlay_package_id;
+
+  // Padding to ensure 4 byte alignment for target_entry_count
+  uint16_t p0;
+
   uint32_t target_entry_count;
+  uint32_t target_inline_entry_count;
   uint32_t overlay_entry_count;
+
   uint32_t string_pool_index_offset;
-  uint32_t string_pool_length;
 };
 
 struct Idmap_target_entry {
   uint32_t target_id;
-  uint8_t type;
-  uint32_t value;
+  uint32_t overlay_id;
+};
+
+struct Idmap_target_entry_inline {
+  uint32_t target_id;
+  Res_value value;
 };
 
 struct Idmap_overlay_entry {
   uint32_t overlay_id;
   uint32_t target_id;
 };
-#pragma pack(pop)
 
 class AssetManager2;
 
diff --git a/libs/androidfw/tests/CursorWindow_bench.cpp b/libs/androidfw/tests/CursorWindow_bench.cpp
new file mode 100644
index 0000000..f1191c3
--- /dev/null
+++ b/libs/androidfw/tests/CursorWindow_bench.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/CursorWindow.h"
+
+namespace android {
+
+static void BM_CursorWindowWrite(benchmark::State& state, size_t rows, size_t cols) {
+    CursorWindow* w;
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+
+    while (state.KeepRunning()) {
+        w->clear();
+        w->setNumColumns(cols);
+        for (int row = 0; row < rows; row++) {
+            w->allocRow();
+            for (int col = 0; col < cols; col++) {
+                w->putLong(row, col, 0xcafe);
+            }
+        }
+    }
+}
+
+static void BM_CursorWindowWrite4x4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 4, 4);
+}
+BENCHMARK(BM_CursorWindowWrite4x4);
+
+static void BM_CursorWindowWrite1Kx4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 1024, 4);
+}
+BENCHMARK(BM_CursorWindowWrite1Kx4);
+
+static void BM_CursorWindowWrite16Kx4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 16384, 4);
+}
+BENCHMARK(BM_CursorWindowWrite16Kx4);
+
+static void BM_CursorWindowRead(benchmark::State& state, size_t rows, size_t cols) {
+    CursorWindow* w;
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+    w->setNumColumns(cols);
+    for (int row = 0; row < rows; row++) {
+        w->allocRow();
+    }
+
+    while (state.KeepRunning()) {
+        for (int row = 0; row < rows; row++) {
+            for (int col = 0; col < cols; col++) {
+                w->getFieldSlot(row, col);
+            }
+        }
+    }
+}
+
+static void BM_CursorWindowRead4x4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 4, 4);
+}
+BENCHMARK(BM_CursorWindowRead4x4);
+
+static void BM_CursorWindowRead1Kx4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 1024, 4);
+}
+BENCHMARK(BM_CursorWindowRead1Kx4);
+
+static void BM_CursorWindowRead16Kx4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 16384, 4);
+}
+BENCHMARK(BM_CursorWindowRead16Kx4);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/CursorWindow_test.cpp b/libs/androidfw/tests/CursorWindow_test.cpp
new file mode 100644
index 0000000..dfcf76e
--- /dev/null
+++ b/libs/androidfw/tests/CursorWindow_test.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <utility>
+
+#include "androidfw/CursorWindow.h"
+
+#include "TestHelpers.h"
+
+#define CREATE_WINDOW_1K \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 10, &w);
+
+#define CREATE_WINDOW_1K_3X3 \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 10, &w); \
+    ASSERT_EQ(w->setNumColumns(3), OK); \
+    ASSERT_EQ(w->allocRow(), OK); \
+    ASSERT_EQ(w->allocRow(), OK); \
+    ASSERT_EQ(w->allocRow(), OK);
+
+#define CREATE_WINDOW_2M \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+
+static constexpr const size_t kHalfInlineSize = 8192;
+static constexpr const size_t kGiantSize = 1048576;
+
+namespace android {
+
+TEST(CursorWindowTest, Empty) {
+    CREATE_WINDOW_1K;
+
+    ASSERT_EQ(w->getNumRows(), 0);
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->size(), 1 << 10);
+    ASSERT_EQ(w->freeSpace(), 1 << 10);
+}
+
+TEST(CursorWindowTest, SetNumColumns) {
+    CREATE_WINDOW_1K;
+
+    // Once we've locked in columns, we can't adjust
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_NE(w->setNumColumns(5), OK);
+    ASSERT_NE(w->setNumColumns(3), OK);
+    ASSERT_EQ(w->getNumColumns(), 4);
+}
+
+TEST(CursorWindowTest, SetNumColumnsAfterRow) {
+    CREATE_WINDOW_1K;
+
+    // Once we've locked in a row, we can't adjust columns
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_NE(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->getNumColumns(), 0);
+}
+
+TEST(CursorWindowTest, AllocRow) {
+    CREATE_WINDOW_1K;
+
+    ASSERT_EQ(w->setNumColumns(4), OK);
+
+    // Rolling forward means we have less free space
+    ASSERT_EQ(w->getNumRows(), 0);
+    auto before = w->freeSpace();
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_LT(w->freeSpace(), before);
+    ASSERT_EQ(w->getNumRows(), 1);
+
+    // Verify we can unwind
+    ASSERT_EQ(w->freeLastRow(), OK);
+    ASSERT_EQ(w->freeSpace(), before);
+    ASSERT_EQ(w->getNumRows(), 0);
+
+    // Can't unwind when no rows left
+    ASSERT_NE(w->freeLastRow(), OK);
+}
+
+TEST(CursorWindowTest, AllocRowBounds) {
+    CREATE_WINDOW_1K;
+
+    // 60 columns is 960 bytes, which means only a single row can fit
+    ASSERT_EQ(w->setNumColumns(60), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_NE(w->allocRow(), OK);
+}
+
+TEST(CursorWindowTest, StoreNull) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putNull(1, 1), OK);
+    ASSERT_EQ(w->putNull(0, 0), OK);
+
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
+    }
+}
+
+TEST(CursorWindowTest, StoreLong) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putLong(1, 1, 0xf00d), OK);
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xf00d);
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+}
+
+TEST(CursorWindowTest, StoreString) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putString(1, 1, "food", 5), OK);
+    ASSERT_EQ(w->putString(0, 0, "cafe", 5), OK);
+
+    size_t size;
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
+        auto actual = w->getFieldSlotValueString(field, &size);
+        ASSERT_EQ(std::string(actual), "food");
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
+        auto actual = w->getFieldSlotValueString(field, &size);
+        ASSERT_EQ(std::string(actual), "cafe");
+    }
+}
+
+TEST(CursorWindowTest, StoreBounds) {
+    CREATE_WINDOW_1K_3X3;
+
+    // Can't work with values beyond bounds
+    ASSERT_NE(w->putLong(0, 3, 0xcafe), OK);
+    ASSERT_NE(w->putLong(3, 0, 0xcafe), OK);
+    ASSERT_NE(w->putLong(3, 3, 0xcafe), OK);
+    ASSERT_EQ(w->getFieldSlot(0, 3), nullptr);
+    ASSERT_EQ(w->getFieldSlot(3, 0), nullptr);
+    ASSERT_EQ(w->getFieldSlot(3, 3), nullptr);
+}
+
+TEST(CursorWindowTest, Inflate) {
+    CREATE_WINDOW_2M;
+
+    auto before = w->size();
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Scratch buffer that will fit before inflation
+    void* buf = malloc(kHalfInlineSize);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store first object that fits inside
+    memset(buf, 42, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Store second simple value
+    ASSERT_EQ(w->putLong(0, 2, 0xface), OK);
+
+    // Store second object that requires inflation
+    memset(buf, 84, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 3, buf, kHalfInlineSize), OK);
+    ASSERT_GT(w->size(), before);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 42, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xface);
+    }
+    {
+        auto field = w->getFieldSlot(0, 3);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 84, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+}
+
+TEST(CursorWindowTest, ParcelEmpty) {
+    CREATE_WINDOW_2M;
+
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 0);
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->size(), 0);
+    ASSERT_EQ(w->freeSpace(), 0);
+
+    // We can't mutate the window after parceling
+    ASSERT_NE(w->setNumColumns(4), OK);
+    ASSERT_NE(w->allocRow(), OK);
+}
+
+TEST(CursorWindowTest, ParcelSmall) {
+    CREATE_WINDOW_2M;
+
+    auto before = w->size();
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Scratch buffer that will fit before inflation
+    void* buf = malloc(kHalfInlineSize);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store first object that fits inside
+    memset(buf, 42, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Store second object with zero length
+    ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Force through a parcel
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 1);
+    ASSERT_EQ(w->getNumColumns(), 4);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 42, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, 0);
+        ASSERT_NE(actual, nullptr);
+    }
+}
+
+TEST(CursorWindowTest, ParcelLarge) {
+    CREATE_WINDOW_2M;
+
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store object that forces inflation
+    void* buf = malloc(kGiantSize);
+    memset(buf, 42, kGiantSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kGiantSize), OK);
+
+    // Store second object with zero length
+    ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
+
+    // Force through a parcel
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 1);
+    ASSERT_EQ(w->getNumColumns(), 4);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kGiantSize);
+        memset(buf, 42, kGiantSize);
+        ASSERT_EQ(memcmp(buf, actual, kGiantSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, 0);
+        ASSERT_NE(actual, nullptr);
+    }
+}
+
+} // android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index f1ed592..c9bf252 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 29c5eb6..3ab244e 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index aeb096d..4aee6b9 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -238,6 +238,16 @@
     return reinterpret_cast<jlong>(font->font.get());
 }
 
+// Critical Native
+static jboolean Font_isSameBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong lFontHandle,
+                                         jlong rFontHandle) {
+    FontWrapper* lFont = reinterpret_cast<FontWrapper*>(lFontHandle);
+    FontWrapper* rFont = reinterpret_cast<FontWrapper*>(rFontHandle);
+    const void* lBufferPtr = lFont->font->typeface()->GetFontData();
+    const void* rBufferPtr = rFont->font->typeface()->GetFontData();
+    return lBufferPtr == rBufferPtr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 struct FontBufferWrapper {
@@ -287,6 +297,7 @@
     { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
     { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath },
     { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
+    { "nIsSameBufferAddress", "(JJ)Z", (void*) Font_isSameBufferAddress },
 };
 
 static const JNINativeMethod gFontBufferHelperMethods[] = {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1ac9922..eacabfd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -465,6 +465,7 @@
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         // Notify the callbacks, even if there's nothing to draw so they aren't waiting
         // indefinitely
+        waitOnFences();
         for (auto& func : mFrameCompleteCallbacks) {
             std::invoke(func, mFrameNumber);
         }
diff --git a/media/OWNERS b/media/OWNERS
index 0fc781c..e741490 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,4 +1,3 @@
-andrewlewis@google.com
 chz@google.com
 elaurent@google.com
 essick@google.com
@@ -18,5 +17,12 @@
 nchalko@google.com
 philburk@google.com
 quxiangfang@google.com
-sungsoo@google.com
 wonsik@google.com
+
+# LON
+andrewlewis@google.com
+aquilescanta@google.com
+olly@google.com
+
+# SEO
+sungsoo@google.com
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 8845d69..f9cbdd4 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -70,7 +70,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
@@ -2088,28 +2087,18 @@
 
         FileInputStream in = null;
         FileOutputStream out = null;
-        File originalFile = null;
-        if (mFilename != null) {
-            originalFile = new File(mFilename);
-        }
         File tempFile = null;
         try {
-            // Move the original file to temporary file.
+            // Copy the original file to temporary file.
+            tempFile = File.createTempFile("temp", "tmp");
             if (mFilename != null) {
-                String parent = originalFile.getParent();
-                String name = originalFile.getName();
-                String tempPrefix = UUID.randomUUID().toString() + "_";
-                tempFile = new File(parent, tempPrefix + name);
-                if (!originalFile.renameTo(tempFile)) {
-                    throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
-                }
+                in = new FileInputStream(mFilename);
             } else if (mSeekableFileDescriptor != null) {
-                tempFile = File.createTempFile("temp", "tmp");
                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                 in = new FileInputStream(mSeekableFileDescriptor);
-                out = new FileOutputStream(tempFile);
-                copy(in, out);
             }
+            out = new FileOutputStream(tempFile);
+            copy(in, out);
         } catch (Exception e) {
             throw new IOException("Failed to copy original file to temp file", e);
         } finally {
@@ -2139,12 +2128,23 @@
                 }
             }
         } catch (Exception e) {
+            // Restore original file
+            in = new FileInputStream(tempFile);
             if (mFilename != null) {
-                if (!tempFile.renameTo(originalFile)) {
-                    throw new IOException("Couldn't restore original file: "
-                            + originalFile.getAbsolutePath());
+                out = new FileOutputStream(mFilename);
+            } else if (mSeekableFileDescriptor != null) {
+                try {
+                    Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
+                } catch (ErrnoException exception) {
+                    throw new IOException("Failed to save new file. Original file may be "
+                            + "corrupted since error occurred while trying to restore it.",
+                            exception);
                 }
+                out = new FileOutputStream(mSeekableFileDescriptor);
             }
+            copy(in, out);
+            closeQuietly(in);
+            closeQuietly(out);
             throw new IOException("Failed to save new file", e);
         } finally {
             closeQuietly(in);
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index e17e069..f582d2a 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -609,25 +609,6 @@
     }
 
     /**
-     * Return true if this is considered an active playback state.
-     *
-     * @hide
-     */
-    public static boolean isActiveState(int state) {
-        switch (state) {
-            case PlaybackState.STATE_FAST_FORWARDING:
-            case PlaybackState.STATE_REWINDING:
-            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
-            case PlaybackState.STATE_SKIPPING_TO_NEXT:
-            case PlaybackState.STATE_BUFFERING:
-            case PlaybackState.STATE_CONNECTING:
-            case PlaybackState.STATE_PLAYING:
-                return true;
-        }
-        return false;
-    }
-
-    /**
      * Returns whether the given bundle includes non-framework Parcelables.
      */
     static boolean hasCustomParcelable(@Nullable Bundle bundle) {
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 8dd6127..b1a88ed 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -480,6 +481,25 @@
         return mExtras;
     }
 
+    /**
+     * Returns whether this is considered as an active playback state.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public boolean isActiveState() {
+        switch (mState) {
+            case PlaybackState.STATE_FAST_FORWARDING:
+            case PlaybackState.STATE_REWINDING:
+            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+            case PlaybackState.STATE_SKIPPING_TO_NEXT:
+            case PlaybackState.STATE_BUFFERING:
+            case PlaybackState.STATE_CONNECTING:
+            case PlaybackState.STATE_PLAYING:
+                return true;
+        }
+        return false;
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState> CREATOR =
             new Parcelable.Creator<PlaybackState>() {
         @Override
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 56499e2..1881e38 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -775,7 +775,8 @@
      *
      * <p>This retrieve the statuses of the frontend for given status types.
      *
-     * @param statusTypes an array of status types which the caller requests.
+     * @param statusTypes an array of status types which the caller requests. Any types that are not
+     *        in {@link FrontendInfo.getStatusCapabilities()} would be ignored.
      * @return statuses which response the caller's requests. {@code null} if the operation failed.
      */
     @Nullable
@@ -1047,6 +1048,20 @@
         }
     }
 
+    private void onModulationReported(int modulation) {
+        if (mScanCallbackExecutor != null && mScanCallback != null) {
+            mScanCallbackExecutor.execute(
+                    () -> mScanCallback.onModulationReported(modulation));
+        }
+    }
+
+    private void onPriorityReported(boolean isHighPriority) {
+        if (mScanCallbackExecutor != null && mScanCallback != null) {
+            mScanCallbackExecutor.execute(
+                    () -> mScanCallback.onPriorityReported(isHighPriority));
+        }
+    }
+
     /**
      * Opens a filter object based on the given types and buffer size.
      *
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index 2649fcf..62d55f5 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
@@ -101,6 +102,7 @@
      * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would return
      * default value. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
+    @IntRange(from = 0, to = 0xefff)
     public int getIpFilterContextId() {
         return mIpFilterContextId;
     }
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 57a04fd..91be5c3 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -187,7 +187,6 @@
 
     /**
      * Releases the MediaEvent object.
-     * @hide
      */
     public void release() {
         synchronized (mLock) {
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 343dbb1..fadc004 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -260,10 +260,12 @@
     private final int mVcmMode;
     // Dvbs scan type is only supported in Tuner 1.1 or higher.
     private final int mScanType;
+    // isDiseqcRxMessage is only supported in Tuner 1.1 or higher.
+    private final boolean mIsDiseqcRxMessage;
 
     private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate,
             int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm,
-            int scanType) {
+            int scanType, boolean isDiseqcRxMessage) {
         super(frequency);
         mModulation = modulation;
         mCodeRate = codeRate;
@@ -274,6 +276,7 @@
         mStandard = standard;
         mVcmMode = vcm;
         mScanType = scanType;
+        mIsDiseqcRxMessage = isDiseqcRxMessage;
     }
 
     /**
@@ -337,6 +340,15 @@
     public int getScanType() {
         return mScanType;
     }
+    /**
+     * To receive Diseqc Message or not. Default value is false.
+     *
+     * The setter {@link Builder#setDiseqcRxMessage(boolean)} is only supported with Tuner HAL 1.1
+     * or higher.
+     */
+    public boolean isDiseqcRxMessage() {
+        return mIsDiseqcRxMessage;
+    }
 
     /**
      * Creates a builder for {@link DvbsFrontendSettings}.
@@ -360,6 +372,7 @@
         private int mStandard = STANDARD_AUTO;
         private int mVcmMode = VCM_MODE_UNDEFINED;
         private int mScanType = SCAN_TYPE_UNDEFINED;
+        private boolean mIsDiseqcRxMessage = false;
 
         private Builder() {
         }
@@ -395,6 +408,21 @@
         }
 
         /**
+         * Set true to receive Diseqc Message.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         */
+        @NonNull
+        public Builder setDiseqcRxMessage(boolean isDiseqcRxMessage) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setDiseqcRxMessage")) {
+                mIsDiseqcRxMessage = isDiseqcRxMessage;
+            }
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          *
          * <p>Default value is {@link #MODULATION_UNDEFINED}.
@@ -481,7 +509,8 @@
         @NonNull
         public DvbsFrontendSettings build() {
             return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
-                    mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode, mScanType);
+                    mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode, mScanType,
+                    mIsDiseqcRxMessage);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index c265bb9..dd9347c 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Lnb;
+import android.media.tv.tuner.TunerVersionChecker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -43,7 +44,13 @@
             FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
             FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_MER,
             FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
-            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO,
+            FRONTEND_STATUS_TYPE_BERS, FRONTEND_STATUS_TYPE_CODERATES,
+            FRONTEND_STATUS_TYPE_BANDWIDTH, FRONTEND_STATUS_TYPE_GUARD_INTERVAL,
+            FRONTEND_STATUS_TYPE_TRANSMISSION_MODE, FRONTEND_STATUS_TYPE_UEC,
+            FRONTEND_STATUS_TYPE_T2_SYSTEM_ID, FRONTEND_STATUS_TYPE_INTERLEAVINGS,
+            FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS, FRONTEND_STATUS_TYPE_TS_DATA_RATES,
+            FRONTEND_STATUS_TYPE_MODULATIONS_EXT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendStatusType {}
 
@@ -145,10 +152,83 @@
      */
     public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
             Constants.FrontendStatusType.ATSC3_PLP_INFO;
-
+    /**
+     * BERS Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_BERS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BERS;
+    /**
+     * Coderate Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_CODERATES =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.CODERATES;
+    /**
+     * Bandwidth Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_BANDWIDTH =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BANDWIDTH;
+    /**
+     * Guard Interval Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.GUARD_INTERVAL;
+    /**
+     * Transmission Mode Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TRANSMISSION_MODE;
+    /**
+     * UEC Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_UEC =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.UEC;
+    /**
+     * T2 System Id Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.T2_SYSTEM_ID;
+    /**
+     * Interleavings Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.INTERLEAVINGS;
+    /**
+     * ISDBT Segments Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.ISDBT_SEGMENTS;
+    /**
+     * TS Data Rates Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TS_DATA_RATES;
+    /**
+     * Extended Modulations Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.MODULATIONS;
 
     /** @hide */
     @IntDef(value = {
+            AtscFrontendSettings.MODULATION_UNDEFINED,
+            AtscFrontendSettings.MODULATION_AUTO,
+            AtscFrontendSettings.MODULATION_MOD_8VSB,
+            AtscFrontendSettings.MODULATION_MOD_16VSB,
+            Atsc3FrontendSettings.MODULATION_UNDEFINED,
+            Atsc3FrontendSettings.MODULATION_AUTO,
+            Atsc3FrontendSettings.MODULATION_MOD_QPSK,
+            Atsc3FrontendSettings.MODULATION_MOD_16QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_64QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_256QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_1024QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_4096QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_UNDEFINED,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_AUTO,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM_NR,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_16QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_32QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_64QAM,
             DvbcFrontendSettings.MODULATION_UNDEFINED,
             DvbcFrontendSettings.MODULATION_AUTO,
             DvbcFrontendSettings.MODULATION_MOD_16QAM,
@@ -171,6 +251,16 @@
             DvbsFrontendSettings.MODULATION_MOD_128APSK,
             DvbsFrontendSettings.MODULATION_MOD_256APSK,
             DvbsFrontendSettings.MODULATION_MOD_RESERVED,
+            DvbtFrontendSettings.CONSTELLATION_UNDEFINED,
+            DvbtFrontendSettings.CONSTELLATION_AUTO,
+            DvbtFrontendSettings.CONSTELLATION_QPSK,
+            DvbtFrontendSettings.CONSTELLATION_16QAM,
+            DvbtFrontendSettings.CONSTELLATION_64QAM,
+            DvbtFrontendSettings.CONSTELLATION_256QAM,
+            DvbtFrontendSettings.CONSTELLATION_QPSK_R,
+            DvbtFrontendSettings.CONSTELLATION_16QAM_R,
+            DvbtFrontendSettings.CONSTELLATION_64QAM_R,
+            DvbtFrontendSettings.CONSTELLATION_256QAM_R,
             IsdbsFrontendSettings.MODULATION_UNDEFINED,
             IsdbsFrontendSettings.MODULATION_AUTO,
             IsdbsFrontendSettings.MODULATION_MOD_BPSK,
@@ -192,6 +282,101 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendModulation {}
 
+    /** @hide */
+    @IntDef(value = {
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_CTI,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_HTI,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_240,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_720,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_0,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_1,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_64_2,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_32_4,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_16_8,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_8_16,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_2,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_3,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_4})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendInterleaveMode {}
+
+    /** @hide */
+    @IntDef(value = {
+            Atsc3FrontendSettings.BANDWIDTH_UNDEFINED,
+            Atsc3FrontendSettings.BANDWIDTH_AUTO,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_6MHZ,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_7MHZ,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_8MHZ,
+            DtmbFrontendSettings.BANDWIDTH_UNDEFINED,
+            DtmbFrontendSettings.BANDWIDTH_AUTO,
+            DtmbFrontendSettings.BANDWIDTH_6MHZ,
+            DtmbFrontendSettings.BANDWIDTH_8MHZ,
+            DvbtFrontendSettings.BANDWIDTH_UNDEFINED,
+            DvbtFrontendSettings.BANDWIDTH_AUTO,
+            DvbtFrontendSettings.BANDWIDTH_8MHZ,
+            DvbtFrontendSettings.BANDWIDTH_7MHZ,
+            DvbtFrontendSettings.BANDWIDTH_6MHZ,
+            DvbtFrontendSettings.BANDWIDTH_5MHZ,
+            DvbtFrontendSettings.BANDWIDTH_1_7MHZ,
+            DvbtFrontendSettings.BANDWIDTH_10MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_UNDEFINED,
+            IsdbtFrontendSettings.BANDWIDTH_AUTO,
+            IsdbtFrontendSettings.BANDWIDTH_8MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_7MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_6MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendBandwidth {}
+
+    /** @hide */
+    @IntDef(value = {
+            DtmbFrontendSettings.TRANSMISSION_MODE_UNDEFINED,
+            DtmbFrontendSettings.TRANSMISSION_MODE_AUTO,
+            DtmbFrontendSettings.TRANSMISSION_MODE_C1,
+            DtmbFrontendSettings.TRANSMISSION_MODE_C3780,
+            DvbtFrontendSettings.TRANSMISSION_MODE_UNDEFINED,
+            DvbtFrontendSettings.TRANSMISSION_MODE_AUTO,
+            DvbtFrontendSettings.TRANSMISSION_MODE_2K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_8K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_4K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_1K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_16K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_32K,
+            IsdbtFrontendSettings.MODE_UNDEFINED,
+            IsdbtFrontendSettings.MODE_AUTO,
+            IsdbtFrontendSettings.MODE_1,
+            IsdbtFrontendSettings.MODE_2,
+            IsdbtFrontendSettings.MODE_3})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendTransmissionMode {}
+
+    /** @hide */
+    @IntDef(value = {
+            DtmbFrontendSettings.GUARD_INTERVAL_UNDEFINED,
+            DtmbFrontendSettings.GUARD_INTERVAL_AUTO,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_420_VARIOUS,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_595_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_945_VARIOUS,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_420_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_945_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_RESERVED,
+            DvbtFrontendSettings.GUARD_INTERVAL_UNDEFINED,
+            DvbtFrontendSettings.GUARD_INTERVAL_AUTO,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_32,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_16,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_8,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_4,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_128,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_128,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_256,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_128})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendGuardInterval {}
 
     private Boolean mIsDemodLocked;
     private Integer mSnr;
@@ -215,6 +400,18 @@
     private Integer mHierarchy;
     private Boolean mIsRfLocked;
     private Atsc3PlpTuningInfo[] mPlpInfo;
+    private int[] mBers;
+    private int[] mCodeRates;
+    private Integer mBandwidth;
+    private Integer mGuardInterval;
+    private Integer mTransmissionMode;
+    private Integer mUec;
+    private Integer mSystemId;
+    private int[] mInterleaving;
+    private int[] mTsDataRate;
+    private int[] mIsdbtSegment;
+    private int[] mModulationsExt;
+
 
     // Constructed and fields set by JNI code.
     private FrontendStatus() {
@@ -323,7 +520,7 @@
     /**
      * Gets Spectral Inversion for DVBC.
      */
-    @DvbcFrontendSettings.SpectralInversion
+    @FrontendSettings.FrontendSpectralInversion
     public int getSpectralInversion() {
         if (mInversion == null) {
             throw new IllegalStateException();
@@ -437,6 +634,182 @@
     }
 
     /**
+     * Gets an array of BERS status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getBers() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getBers status");
+        if (mBers == null) {
+            throw new IllegalStateException();
+        }
+        return mBers;
+    }
+
+    /**
+     * Gets an array of code rates status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getCodeRates() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getCodeRates status");
+        if (mCodeRates == null) {
+            throw new IllegalStateException();
+        }
+        return mCodeRates;
+    }
+
+    /**
+     * Gets bandwidth status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendBandwidth
+    public int getBandwidth() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getBandwidth status");
+        if (mBandwidth == null) {
+            throw new IllegalStateException();
+        }
+        return mBandwidth;
+    }
+
+    /**
+     * Gets guard interval status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendGuardInterval
+    public int getGuardInterval() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getGuardInterval status");
+        if (mGuardInterval == null) {
+            throw new IllegalStateException();
+        }
+        return mGuardInterval;
+    }
+
+    /**
+     * Gets tansmission mode status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendTransmissionMode
+    public int getTransmissionMode() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getTransmissionMode status");
+        if (mTransmissionMode == null) {
+            throw new IllegalStateException();
+        }
+        return mTransmissionMode;
+    }
+
+    /**
+     * Gets UEC status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    public int getUec() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getUec status");
+        if (mUec == null) {
+            throw new IllegalStateException();
+        }
+        return mUec;
+    }
+
+    /**
+     * Gets system id status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    public int getSystemId() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getSystemId status");
+        if (mSystemId == null) {
+            throw new IllegalStateException();
+        }
+        return mSystemId;
+    }
+
+    /**
+     * Gets an array of interleaving status. Array value should be withink {@link
+     * FrontendInterleaveMode}.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getInterleaving() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getInterleaving status");
+        if (mInterleaving == null) {
+            throw new IllegalStateException();
+        }
+        return mInterleaving;
+    }
+
+    /**
+     * Gets an array of isdbt segment status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getIsdbtSegment() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getIsdbtSegment status");
+        if (mIsdbtSegment == null) {
+            throw new IllegalStateException();
+        }
+        return mIsdbtSegment;
+    }
+
+    /**
+     * Gets an array of TS data rate status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getTsDataRate() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getTsDataRate status");
+        if (mTsDataRate == null) {
+            throw new IllegalStateException();
+        }
+        return mTsDataRate;
+    }
+
+    /**
+     * Gets an array of the extended modulations status. Array value should be withink {@link
+     * FrontendModulation}.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getModulationsExt() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getModulationsExt status");
+        if (mModulationsExt == null) {
+            throw new IllegalStateException();
+        }
+        return mModulationsExt;
+    }
+
+    /**
      * Status for each tuning Physical Layer Pipes.
      */
     public static class Atsc3PlpTuningInfo {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index b0491fd..c1400c8 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.util.Log;
 
 /**
  * Scan callback.
@@ -27,6 +28,8 @@
  */
 @SystemApi
 public interface ScanCallback {
+    /** @hide **/
+    String TAG = "ScanCallback";
 
     /** Scan locked the signal. */
     void onLocked();
@@ -70,4 +73,13 @@
     /** Frontend signal type. */
     void onSignalTypeReported(@AnalogFrontendSettings.SignalType int signalType);
 
+    /** Frontend modulation reported. */
+    default void onModulationReported(@FrontendStatus.FrontendModulation int modulation) {
+        Log.d(TAG, "Received modulation scan message");
+    }
+
+    /** Frontend scan message priority reported. */
+    default void onPriorityReported(boolean isHighPriority) {
+        Log.d(TAG, "Received priority scan message: isHighPriority=" + isHighPriority);
+    }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6a1aced..9b86b52 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -136,6 +136,7 @@
 using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
 using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag;
 using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
 using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
@@ -149,7 +150,13 @@
 using ::android::hardware::tv::tuner::V1_1::FrontendDtmbSettings;
 using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
 using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
 using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
 
 struct fields_t {
     jfieldID tunerContext;
@@ -1862,18 +1869,49 @@
     }
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jsize size = env->GetArrayLength(types);
-    std::vector<FrontendStatusType> v(size);
-    env->GetIntArrayRegion(types, 0, size, reinterpret_cast<jint*>(&v[0]));
+    jint intTypes[size];
+    env->GetIntArrayRegion(types, 0, size, intTypes);
+    std::vector<FrontendStatusType> v;
+    std::vector<FrontendStatusTypeExt1_1> v_1_1;
+    for (int i = 0; i < size; i++) {
+        if (isV1_1ExtendedStatusType(intTypes[i])) {
+            v_1_1.push_back(static_cast<FrontendStatusTypeExt1_1>(intTypes[i]));
+        } else {
+            v.push_back(static_cast<FrontendStatusType>(intTypes[i]));
+        }
+    }
 
     Result res;
     hidl_vec<FrontendStatus> status;
-    mFe->getStatus(v,
-            [&](Result r, const hidl_vec<FrontendStatus>& s) {
-                res = r;
-                status = s;
-            });
-    if (res != Result::SUCCESS) {
-        return NULL;
+    hidl_vec<FrontendStatusExt1_1> status_1_1;
+
+    if (v.size() > 0) {
+        mFe->getStatus(v,
+                [&](Result r, const hidl_vec<FrontendStatus>& s) {
+                    res = r;
+                    status = s;
+                });
+        if (res != Result::SUCCESS) {
+            return NULL;
+        }
+    }
+
+    if (v_1_1.size() > 0) {
+        sp<::android::hardware::tv::tuner::V1_1::IFrontend> iFeSp_1_1;
+        iFeSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+
+        if (iFeSp_1_1 != NULL) {
+            iFeSp_1_1->getStatusExt1_1(v_1_1,
+                [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
+                    res = r;
+                    status_1_1 = s;
+                });
+            if (res != Result::SUCCESS) {
+                return NULL;
+            }
+        } else {
+            ALOGW("getStatusExt1_1 is not supported with the current HAL implementation.");
+        }
     }
 
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus");
@@ -2101,9 +2139,274 @@
         }
     }
 
+    for (auto s : status_1_1) {
+        switch(s.getDiscriminator()) {
+            case FrontendStatusExt1_1::hidl_discriminator::modulations: {
+                jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I");
+                std::vector<FrontendModulation> v = s.modulations();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                bool valid = false;
+                jint m[1];
+                for (int i = 0; i < v.size(); i++) {
+                    auto modulation = v[i];
+                    switch(modulation.getDiscriminator()) {
+                        case FrontendModulation::hidl_discriminator::dvbc: {
+                            m[0] = static_cast<jint>(modulation.dvbc());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dvbs: {
+                            m[0] = static_cast<jint>(modulation.dvbs());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                           break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dvbt: {
+                            m[0] = static_cast<jint>(modulation.dvbt());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbs: {
+                            m[0] = static_cast<jint>(modulation.isdbs());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbs3: {
+                            m[0] = static_cast<jint>(modulation.isdbs3());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbt: {
+                            m[0] = static_cast<jint>(modulation.isdbt());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::atsc: {
+                            m[0] = static_cast<jint>(modulation.atsc());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::atsc3: {
+                            m[0] = static_cast<jint>(modulation.atsc3());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dtmb: {
+                            m[0] = static_cast<jint>(modulation.dtmb());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        default:
+                            break;
+                    }
+                }
+                if (valid) {
+                    env->SetObjectField(statusObj, field, valObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::bers: {
+                jfieldID field = env->GetFieldID(clazz, "mBers", "[I");
+                std::vector<uint32_t> v = s.bers();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::codeRates: {
+                jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I");
+                std::vector<::android::hardware::tv::tuner::V1_1::FrontendInnerFec> v
+                        = s.codeRates();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::bandwidth: {
+                jfieldID field = env->GetFieldID(clazz, "mBandwidth", "Ljava/lang/Integer;");
+                auto bandwidth = s.bandwidth();
+                jint intBandwidth;
+                bool valid = true;
+                switch(bandwidth.getDiscriminator()) {
+                    case FrontendBandwidth::hidl_discriminator::atsc3: {
+                        intBandwidth = static_cast<jint>(bandwidth.atsc3());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::dvbt: {
+                        intBandwidth = static_cast<jint>(bandwidth.dvbt());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::isdbt: {
+                        intBandwidth = static_cast<jint>(bandwidth.isdbt());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::dtmb: {
+                        intBandwidth = static_cast<jint>(bandwidth.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intBandwidth);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::interval: {
+                jfieldID field = env->GetFieldID(clazz, "mGuardInterval", "Ljava/lang/Integer;");
+                auto interval = s.interval();
+                jint intInterval;
+                bool valid = true;
+                switch(interval.getDiscriminator()) {
+                    case FrontendGuardInterval::hidl_discriminator::dvbt: {
+                        intInterval = static_cast<jint>(interval.dvbt());
+                        break;
+                    }
+                    case FrontendGuardInterval::hidl_discriminator::isdbt: {
+                        intInterval = static_cast<jint>(interval.isdbt());
+                        break;
+                    }
+                    case FrontendGuardInterval::hidl_discriminator::dtmb: {
+                        intInterval = static_cast<jint>(interval.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intInterval);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::transmissionMode: {
+                jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;");
+                auto transmissionMode = s.transmissionMode();
+                jint intTransmissionMode;
+                bool valid = true;
+                switch(transmissionMode.getDiscriminator()) {
+                    case FrontendTransmissionMode::hidl_discriminator::dvbt: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.dvbt());
+                        break;
+                    }
+                    case FrontendTransmissionMode::hidl_discriminator::isdbt: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.isdbt());
+                        break;
+                    }
+                    case FrontendTransmissionMode::hidl_discriminator::dtmb: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intTransmissionMode);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::uec: {
+                jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;");
+                jobject newIntegerObj = env->NewObject(
+                        intClazz, initInt, static_cast<jint>(s.uec()));
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::systemId: {
+                jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;");
+                jobject newIntegerObj = env->NewObject(
+                        intClazz, initInt, static_cast<jint>(s.systemId()));
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::interleaving: {
+                jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I");
+
+                std::vector<FrontendInterleaveMode> v = s.interleaving();
+                jintArray valObj = env->NewIntArray(v.size());
+                bool valid = false;
+                jint in[1];
+                for (int i = 0; i < v.size(); i++) {
+                    auto interleaving = v[i];
+                    switch(interleaving.getDiscriminator()) {
+                        case FrontendInterleaveMode::hidl_discriminator::atsc3: {
+                            in[0] = static_cast<jint>(interleaving.atsc3());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendInterleaveMode::hidl_discriminator::dvbc: {
+                            in[0] = static_cast<jint>(interleaving.dvbc());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                           break;
+                        }
+                        case FrontendInterleaveMode::hidl_discriminator::dtmb: {
+                            in[0] = static_cast<jint>(interleaving.dtmb());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                           break;
+                        }
+                        default:
+                            break;
+                    }
+                }
+                if (valid) {
+                    env->SetObjectField(statusObj, field, valObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::isdbtSegment: {
+                jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I");
+                std::vector<uint8_t> v = s.isdbtSegment();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::tsDataRate: {
+                jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I");
+                std::vector<uint32_t> v = s.tsDataRate();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            default: {
+                break;
+            }
+        }
+    }
     return statusObj;
 }
 
+bool JTuner::isV1_1ExtendedStatusType(int type) {
+    return (type > static_cast<int>(FrontendStatusType::ATSC3_PLP_INFO)
+                && type <= static_cast<int>(FrontendStatusTypeExt1_1::TS_DATA_RATES));
+}
+
 jint JTuner::closeFrontend() {
     Result r = Result::SUCCESS;
     if (mFe != NULL) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 2b73f31..3f74d24 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -268,6 +268,8 @@
     static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
     static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
     static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
+
+    bool isV1_1ExtendedStatusType(jint type);
 };
 
 class C2DataIdInfo : public C2Param {
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index bf2f613..babd54d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -1613,6 +1613,7 @@
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
+    field public static final int windowLayoutAffinity = 16844313; // 0x1010619
     field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
     field public static final int windowLightNavigationBar = 16844140; // 0x101056c
     field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -11714,10 +11715,7 @@
     method public android.graphics.drawable.Drawable getIcon(int);
     method public CharSequence getLabel();
     method public String getName();
-    method public float getProgress();
     method public android.os.UserHandle getUser();
-    method public boolean isLoading();
-    method public boolean isStartable();
   }
 
   public class LauncherApps {
@@ -11757,7 +11755,6 @@
     ctor public LauncherApps.Callback();
     method public abstract void onPackageAdded(String, android.os.UserHandle);
     method public abstract void onPackageChanged(String, android.os.UserHandle);
-    method public void onPackageProgressChanged(@NonNull String, @NonNull android.os.UserHandle, float);
     method public abstract void onPackageRemoved(String, android.os.UserHandle);
     method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean);
     method public void onPackagesSuspended(String[], android.os.UserHandle);
@@ -30016,9 +30013,12 @@
     method public int describeContents();
     method @NonNull public byte[] getKey();
     method @NonNull public String getName();
+    method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String AUTH_AES_XCBC = "xcbc(aes)";
     field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+    field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
     field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30026,6 +30026,7 @@
     field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
     field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
     field public static final String CRYPT_AES_CBC = "cbc(aes)";
+    field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
   }
 
   public final class IpSecManager {
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 3c1d19f..9b83a29 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -93,6 +93,10 @@
     field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0
   }
 
+  public final class PlaybackState implements android.os.Parcelable {
+    method public boolean isActiveState();
+  }
+
 }
 
 package android.os {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index f558b48..93fdfba 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -225,6 +225,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -594,7 +595,7 @@
 
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
-    method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method public void setDontSendToRestrictedApps(boolean);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long);
     method public android.os.Bundle toBundle();
@@ -1742,6 +1743,7 @@
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
+    field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
@@ -5231,7 +5233,7 @@
     method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
     method public int getDstPort();
-    method public int getIpFilterContextId();
+    method @IntRange(from=0, to=61439) public int getIpFilterContextId();
     method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
     method public int getSrcPort();
     method public int getType();
@@ -5267,6 +5269,7 @@
     method public boolean isPrivateData();
     method public boolean isPtsPresent();
     method public boolean isSecureMemory();
+    method public void release();
   }
 
   public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5773,6 +5776,7 @@
     method public int getSymbolRate();
     method public int getType();
     method public int getVcmMode();
+    method public boolean isDiseqcRxMessage();
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
     field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5816,6 +5820,7 @@
   public static class DvbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
@@ -6017,14 +6022,21 @@
   public class FrontendStatus {
     method public int getAgc();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
+    method public int getBandwidth();
     method public int getBer();
+    method @NonNull public int[] getBers();
+    method @NonNull public int[] getCodeRates();
     method public int getFreqOffset();
+    method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
+    method @NonNull public int[] getInterleaving();
+    method @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
+    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -6033,23 +6045,34 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getSystemId();
+    method public int getTransmissionMode();
+    method @NonNull public int[] getTsDataRate();
+    method public int getUec();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLocked();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
     field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
+    field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
+    field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
     field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
     field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
+    field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+    field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
     field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
+    field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
@@ -6059,6 +6082,10 @@
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
+    field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
+    field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
+    field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20
+    field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c
   }
 
   public static class FrontendStatus.Atsc3PlpTuningInfo {
@@ -6222,7 +6249,9 @@
     method public void onHierarchyReported(int);
     method public void onInputStreamIdsReported(@NonNull int[]);
     method public void onLocked();
+    method public default void onModulationReported(int);
     method public void onPlpIdsReported(@NonNull int[]);
+    method public default void onPriorityReported(boolean);
     method public void onProgress(@IntRange(from=0, to=100) int);
     method public void onScanStopped();
     method public void onSignalTypeReported(int);
@@ -10302,6 +10331,10 @@
     field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
   }
 
+  public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+  }
+
   public final class SmsCbCmasInfo implements android.os.Parcelable {
     ctor public SmsCbCmasInfo(int, int, int, int, int, int);
     method public int describeContents();
diff --git a/packages/CarSystemUI/samples/sample3/rro/Android.bp b/packages/CarSystemUI/samples/sample3/rro/Android.bp
new file mode 100644
index 0000000..0eae7c2
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2020 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.
+//
+
+android_app {
+    name: "CarSystemUISampleThreeRRO",
+    resource_dirs: ["res"],
+    certificate: "platform",
+    platform_apis: true,
+    manifest: "AndroidManifest.xml",
+    aaptflags: [
+        "--no-resource-deduping",
+        "--no-resource-removal",
+     ]
+}
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml
new file mode 100644
index 0000000..5c25056
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.systemui.rro">
+    <overlay
+        android:targetPackage="com.android.systemui"
+        android:isStatic="false"
+        android:resourcesMap="@xml/car_sysui_overlays"
+    />
+</manifest>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml
new file mode 100644
index 0000000..a8d8a2f
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:viewportWidth="44"
+        android:viewportHeight="44"
+        android:width="44dp"
+        android:height="44dp">
+<path
+    android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
+    android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml
new file mode 100644
index 0000000..c78f0ed
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml
new file mode 100644
index 0000000..55c968e
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M16.34,8.36l-2.29,0.82c-0.18,-0.13 -0.38,-0.25 -0.58,-0.34c0.17,-0.83 0.63,-1.58 1.36,-2.06C16.85,5.44 16.18,2 13.39,2C9,2 7.16,5.01 8.36,7.66l0.82,2.29c-0.13,0.18 -0.25,0.38 -0.34,0.58c-0.83,-0.17 -1.58,-0.63 -2.06,-1.36C5.44,7.15 2,7.82 2,10.61c0,4.4 3.01,6.24 5.66,5.03l2.29,-0.82c0.18,0.13 0.38,0.25 0.58,0.34c-0.17,0.83 -0.63,1.58 -1.36,2.06C7.15,18.56 7.82,22 10.61,22c4.4,0 6.24,-3.01 5.03,-5.66l-0.82,-2.29c0.13,-0.18 0.25,-0.38 0.34,-0.58c0.83,0.17 1.58,0.63 2.06,1.36c1.34,2.01 4.77,1.34 4.77,-1.45C22,9 18.99,7.16 16.34,8.36zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5c0.83,0 1.5,0.67 1.5,1.5C13.5,12.83 12.83,13.5 12,13.5zM10.24,5.22C10.74,4.44 11.89,4 13.39,4c0.79,0 0.71,0.86 0.34,1.11c-1.22,0.81 -2,2.06 -2.25,3.44c-0.21,0.03 -0.42,0.08 -0.62,0.15l-0.68,-1.88C10,6.42 9.86,5.81 10.24,5.22zM6.83,13.82c-0.4,0.18 -1.01,0.32 -1.61,-0.06C4.44,13.26 4,12.11 4,10.61c0,-0.79 0.86,-0.71 1.11,-0.34c0.81,1.22 2.06,2 3.44,2.25c0.03,0.21 0.08,0.42 0.15,0.62L6.83,13.82zM13.76,18.78c-0.5,0.77 -1.65,1.22 -3.15,1.22c-0.79,0 -0.71,-0.86 -0.34,-1.11c1.22,-0.81 2,-2.06 2.25,-3.44c0.21,-0.03 0.42,-0.08 0.62,-0.15l0.68,1.88C14,17.58 14.14,18.18 13.76,18.78zM18.89,13.73c-0.81,-1.22 -2.06,-2 -3.44,-2.25c-0.03,-0.21 -0.08,-0.42 -0.15,-0.62l1.88,-0.68c0.4,-0.18 1.01,-0.32 1.61,0.06c0.77,0.5 1.22,1.65 1.22,3.15C20,14.19 19.14,14.11 18.89,13.73z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml
new file mode 100644
index 0000000..6339ebb
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M22 5.5L22 24.8416667C20.9183333 24.2183333 19.6716667 23.8333333 18.3333333 23.8333333C14.2816667 23.8333333 11 27.115 11 31.1666667C11 35.2183333 14.2816667 38.5 18.3333333 38.5C22.385 38.5 25.6666667 35.2183333 25.6666667 31.1666667L25.6666667 12.8333333L33 12.8333333L33 5.5L22 5.5Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml
new file mode 100644
index 0000000..e1fabe0
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M39.8016667 20.6983333L23.3016667 4.19833333C22.5866667 3.48333333 21.4316667 3.48333333 20.7166667 4.19833333L4.21666667 20.6983333C3.50166667 21.4133333 3.50166667 22.5683333 4.21666667 23.2833333L20.7166667 39.7833333C21.4316667 40.4983333 22.5866667 40.4983333 23.3016667 39.7833333L39.8016667 23.2833333C40.5166667 22.5866667 40.5166667 21.4316667 39.8016667 20.6983333ZM25.6666667 26.5833333L25.6666667 22L18.3333333 22L18.3333333 27.5L14.6666667 27.5L14.6666667 20.1666667C14.6666667 19.1583333 15.4916667 18.3333333 16.5 18.3333333L25.6666667 18.3333333L25.6666667 13.75L32.0833333 20.1666667L25.6666667 26.5833333Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..aabf916
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="44"
+        android:viewportHeight="44">
+    <path
+        android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..f185eb9
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M36.92857 22.39286A14.53571 14.53571 0 0 1 7.857143 22.39286A14.53571 14.53571 0 0 1 36.92857 22.39286Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="4" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml
new file mode 100644
index 0000000..50e36b5
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M12.1366667 19.7816667C14.7766667 24.97 19.03 29.205 24.2183333 31.8633333L28.2516667 27.83C28.7466667 27.335 29.48 27.17 30.1216667 27.39C32.175 28.0683333 34.3933333 28.435 36.6666667 28.435C37.675 28.435 38.5 29.26 38.5 30.2683333L38.5 36.6666667C38.5 37.675 37.675 38.5 36.6666667 38.5C19.4516667 38.5 5.5 24.5483333 5.5 7.33333333C5.5 6.325 6.325 5.5 7.33333333 5.5L13.75 5.5C14.7583333 5.5 15.5833333 6.325 15.5833333 7.33333333C15.5833333 9.625 15.95 11.825 16.6283333 13.8783333C16.83 14.52 16.6833333 15.235 16.17 15.7483333L12.1366667 19.7816667Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml
new file mode 100644
index 0000000..66da21c
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+    <solid
+        android:color="#404040"
+    />
+</shape>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml
new file mode 100644
index 0000000..3d1cd08
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.car.navigationbar.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/transparent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="50dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.car.navigationbar.CarNavigationBarView>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..8314ba5
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<com.android.systemui.car.navigationbar.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <RelativeLayout
+        android:id="@+id/nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layoutDirection="ltr">
+
+        <com.android.systemui.car.hvac.AdjustableTemperatureView
+            android:id="@+id/driver_hvac"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            systemui:hvacAreaId="49"
+            systemui:hvacTempFormat="%.0f\u00B0" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerInParent="true"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:layoutDirection="ltr"
+            android:paddingEnd="@dimen/system_bar_button_group_padding"
+            android:paddingStart="@dimen/system_bar_button_group_padding">
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/home"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_home"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/phone_nav"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_phone"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+                systemui:packages="com.android.car.dialer"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/grid_nav"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_apps"
+                systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/hvac"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_hvac"
+                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+                systemui:broadcast="true"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/notifications"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_notification"
+                systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/>
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+        </LinearLayout>
+
+        <com.android.systemui.car.hvac.AdjustableTemperatureView
+            android:id="@+id/passenger_hvac"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentEnd="true"
+            android:gravity="center_vertical"
+            systemui:hvacAreaId="68"
+            systemui:hvacTempFormat="%.0f\u00B0" />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/lock_screen_nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:layoutDirection="ltr"
+        android:paddingEnd="@dimen/car_keyline_1"
+        android:paddingStart="@dimen/car_keyline_1"
+        android:visibility="gone"
+    />
+</com.android.systemui.car.navigationbar.CarNavigationBarView>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml
new file mode 100644
index 0000000..bc7ded2
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<resources>
+    <attr name="broadcast" format="boolean"/>
+    <attr name="icon" format="reference"/>
+    <attr name="intent" format="string"/>
+    <attr name="longIntent" format="string"/>
+    <attr name="componentNames" format="string" />
+    <attr name="highlightWhenSelected" format="boolean" />
+    <attr name="categories" format="string"/>
+    <attr name="packages" format="string" />
+
+    <!-- Custom attributes to configure hvac values -->
+    <declare-styleable name="AnimatedTemperatureView">
+        <attr name="hvacAreaId" format="integer"/>
+        <attr name="hvacPropertyId" format="integer"/>
+        <attr name="hvacTempFormat" format="string"/>
+        <!-- how far away the animations should center around -->
+        <attr name="hvacPivotOffset" format="dimension"/>
+        <attr name="hvacMinValue" format="float"/>
+        <attr name="hvacMaxValue" format="float"/>
+        <attr name="hvacMinText" format="string|reference"/>
+        <attr name="hvacMaxText" format="string|reference"/>
+        <attr name="android:gravity"/>
+        <attr name="android:minEms"/>
+        <attr name="android:textAppearance"/>
+    </declare-styleable>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml
new file mode 100644
index 0000000..f98cb96
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="car_nav_icon_fill_color">#8F8F8F</color>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml
new file mode 100644
index 0000000..2148e7c
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<resources>
+    <!-- Configure which system bars should be displayed. -->
+    <bool name="config_enableTopNavigationBar">false</bool>
+    <bool name="config_enableLeftNavigationBar">true</bool>
+    <bool name="config_enableRightNavigationBar">false</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
+
+    <!-- Configure the type of each system bar. Each system bar must have a unique type. -->
+    <!--    STATUS_BAR = 0-->
+    <!--    NAVIGATION_BAR = 1-->
+    <!--    STATUS_BAR_EXTRA = 2-->
+    <!--    NAVIGATION_BAR_EXTRA = 3-->
+    <integer name="config_topSystemBarType">2</integer>
+    <integer name="config_leftSystemBarType">0</integer>
+    <integer name="config_rightSystemBarType">3</integer>
+    <integer name="config_bottomSystemBarType">1</integer>
+
+    <!-- Configure the relative z-order among the system bars. When two system bars overlap (e.g.
+         if both top bar and left bar are enabled, it creates an overlapping space in the upper left
+         corner), the system bar with the higher z-order takes the overlapping space and padding is
+         applied to the other bar.-->
+    <!-- NOTE: If two overlapping system bars have the same z-order, SystemBarConfigs will throw a
+         RuntimeException, since their placing order cannot be determined. Bars that do not overlap
+         are allowed to have the same z-order. -->
+    <!-- NOTE: If the z-order of a bar is 10 or above, it will also appear on top of HUN's.    -->
+    <integer name="config_topSystemBarZOrder">0</integer>
+    <integer name="config_leftSystemBarZOrder">10</integer>
+    <integer name="config_rightSystemBarZOrder">0</integer>
+    <integer name="config_bottomSystemBarZOrder">15</integer>
+
+    <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up
+         notifications will be shown pushed to the top of their parent container. If true, they will
+         be shown pushed to the bottom of their parent container. If true, then should override
+         config_headsUpNotificationAnimationHelper to use a different AnimationHelper, such as
+         com.android.car.notification.headsup.animationhelper.
+         CarHeadsUpNotificationBottomAnimationHelper. -->
+    <bool name="config_showHeadsUpNotificationOnBottom">false</bool>
+
+    <string name="config_notificationPanelViewMediator" translatable="false">com.android.systemui.car.notification.BottomNotificationPanelViewMediator</string>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml
new file mode 100644
index 0000000..c89f949
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <dimen name="car_left_navigation_bar_width">280dp</dimen>
+    <dimen name="car_keyline_1">24dp</dimen>
+    <dimen name="system_bar_button_group_padding">64dp</dimen>
+    <dimen name="system_bar_icon_drawing_size">44dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml
new file mode 100644
index 0000000..bad3691
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="TextAppearance.StatusBar.Clock"
+           parent="@*android:style/TextAppearance.StatusBar.Icon">
+        <item name="android:textSize">40sp</item>
+        <item name="android:fontFamily">sans-serif-regular</item>
+        <item name="android:textColor">#FFFFFF</item>
+    </style>
+
+    <style name="NavigationBarButton">
+        <item name="android:layout_height">96dp</item>
+        <item name="android:layout_width">96dp</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml
new file mode 100644
index 0000000..f08d968
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml
@@ -0,0 +1,75 @@
+
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<overlay>
+    <item target="layout/car_navigation_bar" value="@layout/car_navigation_bar"/>
+    <item target="layout/car_left_navigation_bar" value="@layout/car_left_navigation_bar"/>
+
+    <item target="bool/config_enableTopNavigationBar" value="@bool/config_enableTopNavigationBar"/>
+    <item target="bool/config_enableLeftNavigationBar" value="@bool/config_enableLeftNavigationBar"/>
+    <item target="bool/config_enableRightNavigationBar" value="@bool/config_enableRightNavigationBar"/>
+    <item target="bool/config_enableBottomNavigationBar" value="@bool/config_enableBottomNavigationBar"/>
+    <item target="bool/config_showHeadsUpNotificationOnBottom" value="@bool/config_showHeadsUpNotificationOnBottom"/>
+
+    <item target="attr/icon" value="@attr/icon"/>
+    <item target="attr/intent" value="@attr/intent"/>
+    <item target="attr/longIntent" value="@attr/longIntent"/>
+    <item target="attr/componentNames" value="@attr/componentNames"/>
+    <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/>
+    <item target="attr/categories" value="@attr/categories"/>
+    <item target="attr/packages" value="@attr/packages"/>
+    <item target="attr/hvacAreaId" value="@attr/hvacAreaId"/>
+    <item target="attr/hvacPropertyId" value="@attr/hvacPropertyId"/>
+    <item target="attr/hvacTempFormat" value="@attr/hvacTempFormat"/>
+    <item target="attr/hvacPivotOffset" value="@attr/hvacPivotOffset"/>
+    <item target="attr/hvacMinValue" value="@attr/hvacMinValue"/>
+    <item target="attr/hvacMaxValue" value="@attr/hvacMaxValue"/>
+    <item target="attr/hvacMinText" value="@attr/hvacMinText"/>
+    <item target="attr/hvacMaxText" value="@attr/hvacMaxText"/>
+    <!-- start the intent as a broad cast instead of an activity if true-->
+    <item target="attr/broadcast" value="@attr/broadcast"/>
+
+    <item target="color/car_nav_icon_fill_color" value="@color/car_nav_icon_fill_color" />
+
+    <item target="drawable/car_ic_overview" value="@drawable/car_ic_overview" />
+    <item target="drawable/car_ic_home" value="@drawable/car_ic_home" />
+    <item target="drawable/car_ic_hvac" value="@drawable/car_ic_hvac" />
+    <item target="drawable/car_ic_apps" value="@drawable/car_ic_apps" />
+    <item target="drawable/car_ic_music" value="@drawable/car_ic_music" />
+    <item target="drawable/car_ic_notification" value="@drawable/car_ic_notification" />
+    <item target="drawable/car_ic_phone" value="@drawable/car_ic_phone" />
+    <item target="drawable/car_ic_navigation" value="@drawable/car_ic_navigation" />
+
+    <item target="dimen/car_left_navigation_bar_width" value="@dimen/car_left_navigation_bar_width" />
+    <item target="dimen/car_keyline_1" value="@dimen/car_keyline_1" />
+    <item target="dimen/system_bar_button_group_padding" value="@dimen/system_bar_button_group_padding" />
+    <item target="dimen/system_bar_icon_drawing_size" value="@dimen/system_bar_icon_drawing_size" />
+
+    <item target="integer/config_topSystemBarType" value="@integer/config_topSystemBarType"/>
+    <item target="integer/config_leftSystemBarType" value="@integer/config_leftSystemBarType"/>
+    <item target="integer/config_rightSystemBarType" value="@integer/config_rightSystemBarType"/>
+    <item target="integer/config_bottomSystemBarType" value="@integer/config_bottomSystemBarType"/>
+
+    <item target="integer/config_topSystemBarZOrder" value="@integer/config_topSystemBarZOrder"/>
+    <item target="integer/config_leftSystemBarZOrder" value="@integer/config_leftSystemBarZOrder"/>
+    <item target="integer/config_rightSystemBarZOrder" value="@integer/config_rightSystemBarZOrder"/>
+    <item target="integer/config_bottomSystemBarZOrder" value="@integer/config_bottomSystemBarZOrder"/>
+
+    <item target="string/config_notificationPanelViewMediator" value="@string/config_notificationPanelViewMediator"/>
+
+    <item target="style/NavigationBarButton" value="@style/NavigationBarButton"/>
+</overlay>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
index b17ad0f..b056dcf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.GlobalModule;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.WMModule;
+import com.android.systemui.wmshell.CarWMComponent;
 
 import javax.inject.Singleton;
 
@@ -41,6 +42,12 @@
         CarGlobalRootComponent build();
     }
 
+    /**
+     * Builder for a WMComponent.
+     */
+    @Override
+    CarWMComponent.Builder getWMComponentBuilder();
+
     @Override
     CarSysUIComponent.Builder getSysUIComponent();
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 51fda96..1d35bbb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -64,7 +64,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.wmshell.CarWMShellModule;
 
 import javax.inject.Named;
 
@@ -74,8 +73,7 @@
 
 @Module(
         includes = {
-                QSModule.class,
-                CarWMShellModule.class
+                QSModule.class
         })
 abstract class CarSystemUIModule {
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java
new file mode 100644
index 0000000..c6a7fd2
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.wmshell;
+
+import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.dagger.WMSingleton;
+
+import dagger.Subcomponent;
+
+
+/**
+ * Dagger Subcomponent for WindowManager.
+ */
+@WMSingleton
+@Subcomponent(modules = {CarWMShellModule.class})
+public interface CarWMComponent extends WMComponent {
+
+    /**
+     * Builder for a SysUIComponent.
+     */
+    @Subcomponent.Builder
+    interface Builder extends WMComponent.Builder {
+        CarWMComponent build();
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index 3bfe410..27aabff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.wm.DisplaySystemBarsController;
 import com.android.wm.shell.common.DisplayController;
@@ -35,7 +35,7 @@
 /** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */
 @Module(includes = WMShellBaseModule.class)
 public abstract class CarWMShellModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(Context context,
             IWindowManager wmService, DisplayController displayController,
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 462a6a9..7208894 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloweens"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabies"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 1559fa8..ff9f652 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ስሎቫክ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ስሎቫኒያ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ቱርክኛ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ቱርክኛ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ዩክሬን"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"አረብኛ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ግሪክኛ"</string>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index bf508b2..8ed1972 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"السلوفاكية"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"السلوفينية"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"التركية"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏لوحة المفاتيح باللغة التركية F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"الأوكرانية"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"العربية"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"اليونانية"</string>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index 49fbef9..9744a7d 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"শ্ল\'ভাক"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"শ্ল\'ভেনিয়া"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুৰ্কী"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"টুৰ্কিছ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্ৰেনিয়ান"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আৰবী"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্ৰীক"</string>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index c5a1e1e..ee3a337 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloven"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkcə F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrayna"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Ərəb"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunan"</string>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 16f1cb2..1fc84f3 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenačka"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turska F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 6b0523f..50c5910 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Славацкая"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Славенская"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турэцкая"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турэцкая-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украінская"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабская"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грэчаская"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index a7088c9..5c25f97 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словашки"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенски"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турски"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски (тип F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украински"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабска клавиатурна подредба"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Гръцка клавиатурна подредба"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index f387414..a996538 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"স্লোভাক"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"স্লোভেনিয়ান"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুর্কি"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্রেনীয়"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আরবি"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্রীক"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index b92ac8c..df58464 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovački"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenački"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turski"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinski"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index a5b5e10..761c248 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovac"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Eslovè"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraïnès"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Àrab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 6b4f7eb..3f1b3d0 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turečtina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabština"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"řečtina"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index cf2aecf..b160341 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Græsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 1e78685..95bd806 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakisch"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slowenisch"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkisch"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkisch F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainisch"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griechisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index eb2cc9b..1b9b42e 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Σλοβακικά"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Σλοβενικά"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Τουρκικά"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Τουρκικά F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ουκρανικά"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Αραβικά"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Ελληνικά"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index d088186..92c5a5c 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎Slovak‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎Slovenian‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎Turkish‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎Turkish F‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎Ukrainian‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎Arabic‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎Greek‎‏‎‎‏‎"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index e8d6597..b9fe046 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 9396e46..77b896b 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index cf28e9f..c835522 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türgi"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türgi F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Araabia"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kreeka"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 1e080fc..77d252d 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovakiarra"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveniarra"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiarra"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkiarra (F teklatua)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainarra"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiarra"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 5cb237e..a086060 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"اسلوواکی"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"اسلوونیایی"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکی"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"اوکراینی"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"یونانی"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index da72106..a20416f 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovakki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sloveeni"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turkki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkki (F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabia"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"kreikka"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 45aca35..63635824 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index b55a3c9..0e41a2e 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Clavier turc en F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 40ede04..9995f79 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraíno"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 631fc14..de50fb9 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"સ્લોવૅક"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"સ્લોવેનિયન"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ટર્કીશ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ટર્કિશ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"યુક્રેનિયન"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"અરબી"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ગ્રીક"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 7d1e2f8..55fc5bf 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियाई"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index aff2a37..6832437 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 50d667b4..f2ac8a2 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"szlovák"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"szlovén"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"török"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"török F-billentyűzet"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrán"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"görög"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 4a6fe2b..a6676fe 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Սլովակերեն"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Սլովեներեն"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Թուրքերեն"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"թուրքերեն F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ուկրաիներեն"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Արաբերեն"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Հունարեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 90ba97d..41bf2de 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunani"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 0889b21..b761a7e 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slóvaskt"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slóvenskt"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkneskt"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkneskt F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Úkranískt"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabískt"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grískt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 77f78c6..ed1ec55 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraino"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabo"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greco"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 52641b2..de9b276 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"סלובקית"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"סלובנית"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"טורקית"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏טורקית F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"אוקראינית"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ערבית"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"יוונית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 2961548..6be0b4c 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"スロバキア語"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"スロベニア語"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"トルコ語"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"トルコ語 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ウクライナ語"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"アラビア語"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ギリシャ語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 2ccfeb2..92d9470 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"სლოვაკური"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"სლოვენური"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"თურქული"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"თურქული F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"უკრაინული"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"არაბული"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ბერძნული"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index dfe8c56..c8ab796 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Түрік"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түрік тілі, F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 3bd7f20..4b12321 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ស្លូវ៉ាគី"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ស្លូវ៉ានី"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ទួរគី"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"តួកគី F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"អ៊ុយក្រែន"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"អារ៉ាប់"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ក្រិក"</string>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 1e3c693..761b7cc 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ಸ್ಲೋವಾಕ್"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ಸ್ಲೋವೇನಿಯನ್"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ಟರ್ಕಿಶ್‌"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ಟರ್ಕಿಶ್ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ಉಕ್ರೇನಿಯನ್"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ಅರೇಬಿಕ್"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ಗ್ರೀಕ್"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 1470504..2a1cbb0 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"슬로바키아어"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"슬로베니아어"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"터키어"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"터키어 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"우크라이나어"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"아랍어"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"그리스어"</string>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index cb9dbb2..70295e1 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"түркчө"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түркчө F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабча"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грекче"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 4ae4b7d..2b8946b 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ສະໂລແວັກ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ສະໂລເວນຽນ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ເຕີກິສ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ເທືຄິຊ-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ຢູເຄຣນຽນ"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ອາຣັບ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ກ​ຣີກ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index d2aef7f..5fca46b 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakų k."</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovėnų k."</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkų k."</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkų F k."</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainiečių k."</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabų"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Graikų"</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 8f3ff0a..d225329 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovāku"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovēņu"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turku"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turku F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiņu"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arābu"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieķu"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 9584e15..d98b58b 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словачки"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словенечки"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турски"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украински"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 9e53443..f346881 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"സ്ലോവാക്"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"സ്ലോവേനിയൻ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ടർക്കിഷ്"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ടർക്കിഷ്-എഫ്"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ഉക്രേനിയന്‍"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"അറബിക്"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ഗ്രീക്ക്"</string>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 18c2faf..fcaf321 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словени"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турк"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турк F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украйн"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index d8788c9..c04006d 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोव्हाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोव्हेनियन"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्कीश एफ"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियन"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 8bc9b2a..9bff171 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Bahasa Slovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Bahasa Slovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Bahasa Turki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Bahasa Ukraine"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Bahasa Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Bahasa Greek"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 2672057..01b5507 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"စလိုဗက်"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"စလိုဗေးနီးယန်း"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"တူရကီ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"တူရကီ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ယူကရိန်း"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"အာရပ်"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ဂရိ"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 83b87e5..60cac3d 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gresk"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 4801f75..13740e7 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"टर्किश"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्किस-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"युक्रेनी"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 6e58490..f3a5814 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveens"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index aa16151..52556ef 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ସ୍ଲୋଭାକ୍"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ସ୍ଲୋଭେନିଆନ୍"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ତୁର୍କିସ୍"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ତୁର୍କିଶ୍ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ୟୁକ୍ରାନିଆନ୍"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ଆରବିକ୍‍"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ଗ୍ରୀକ୍"</string>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 7e5a03c..f261fb52 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ਸਲੋਵਾਕ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ਸਲੋਵੀਅਨ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ਤੁਰਕੀ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ਤੁਰਕੀ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ਯੂਕਰੇਨੀਅਨ"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ਅਰਬੀ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ਯੂਨਾਨੀ"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 51755e2..25a3a90 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Słowacki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Słoweński"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turecki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turecka F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiński"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grecki"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 89bb3e3..3ad3e63 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index f7ff250..3867e1c 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacă"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenă"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turcă"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turcă F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraineană"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabă"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greacă"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 7066813..7c5c95a 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацкий"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенский"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецкий"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турецкий (тип F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украинский"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арабский"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"греческий"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index eb3c446..f4147f3 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ස්ලෝවැක්"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ස්ලෝවේනියානු"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"තුර්කි"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"තුර්කි F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"යුක්රේනියානු"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"අරාබි"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ග්‍රීක"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index e7e15b0..301c800 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turečtina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabčina"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gréčtina"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index f4d1e57..09b3c31 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovaška"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turška"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turščina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabščina"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grščina"</string>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 9b4fe9e..0863138 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"sllovakisht"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sllovenisht"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turqisht"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turqisht me F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrainisht"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabisht"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"greqisht"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index e3a2043..973b833 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словачка"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словеначка"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турска"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турска F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украјинска"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 097ada4..a08a74b 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakiskt"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenskt"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiskt"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkiska, F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainskt"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiska"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grekiska"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 3257962..0f4c846 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Kislovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Kislovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Kituruki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Kituruki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Kiukrania"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Kiarabu"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kigiriki"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index d3c6000..f596b40 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ஸ்லோவாக்"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ஸ்லோவேனியன்"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"டர்கிஷ்"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"டர்கிஷ் F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"உக்ரைனியன்"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"அரபிக்"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"கிரேக்கம்"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index c0253e5..5a10f6e 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"స్లోవక్"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"స్లోవేనియన్"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"టర్కిష్"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"టర్కిష్ ఎఫ్"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ఉక్రెయినియన్"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"అరబిక్"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"గ్రీక్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index c8e0e62..5f60c3d 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"สโลวัก"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"สโลวีเนีย"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ตุรกี"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ภาษาตุรกี F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ยูเครน"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ภาษาอารบิค"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"กรีก"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index b9aee76..c42adbc 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index f093abb..2877cb7 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakça"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovence"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkçe"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkçe F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraynaca"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arapça"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunanca"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index d9b58d2..3b0de34 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацька"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенська"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецька"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турецька-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"українська"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабська"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грецька"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 2bff7ed..0cc9b61 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"سلوووک"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"سلووینیائی"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکش"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏ترکی-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"يُوکرينی"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"يونانی"</string>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 9245aeb..161bd0d 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakcha"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovyancha"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkcha"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkcha F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraincha"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grek"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 76fd0bf..0c638fa 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Tiếng Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Tiếng Sloven"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tiếng Thổ Nhĩ Kỳ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tiếng Thổ Nhĩ Kỳ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Tiếng Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Tiếng Ả rập"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Tiếng Hy Lạp"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index aa75605..b249779 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克语"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亚语"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其语"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其语 F 型键盘"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"乌克兰语"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯语"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希腊语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index dc824b9..60a52e9 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亞文"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index c2714da..c3217e3 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛維尼亞文"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 3af1da1..2c53626 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Isi-Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Isi-Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Isi-Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"I-Turkish-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Isi-Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Isi-Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Isi-Greek"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index c5a4f2f..0b21a2d 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -28,7 +28,7 @@
     <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏پیکربندی IP انجام نشد"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"اتصال ناموفق به دلیل شبکه با کیفیت پایین"</string>
     <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏اتصال Wi-Fi برقرار نشد"</string>
-    <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل احراز هویت"</string>
+    <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل اصالت‌سنجی"</string>
     <string name="wifi_cant_connect" msgid="5718417542623056783">"برقراری اتصال ممکن نیست"</string>
     <string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"برقراری اتصال به «<xliff:g id="AP_NAME">%1$s</xliff:g>» ممکن نیست"</string>
     <string name="wifi_check_password_try_again" msgid="8817789642851605628">"گذرواژه را بررسی و دوباره امتحان کنید"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index fbfe84c..7c8dc24 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -25,7 +25,7 @@
     <string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
-    <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फिगरेशन अयशस्वी"</string>
+    <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"कमी दर्जाच्या नेटवर्कमुळे कनेक्ट केलेले नाही"</string>
     <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi कनेक्शन अयशस्वी"</string>
     <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 54adb3a..e34732f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -89,7 +89,7 @@
     <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"सम्पर्क साझेदारी"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"सम्पर्क साझेदारीका लागि प्रयोग"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string>
-    <string name="bluetooth_profile_map" msgid="8907204701162107271">"पाठ सन्देशहरू"</string>
+    <string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM पहुँच"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 1802be6..b971aee 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -197,9 +197,9 @@
     <string name="category_personal" msgid="6236798763159385225">"ذاتی"</string>
     <string name="category_work" msgid="4014193632325996115">"دفتر"</string>
     <string name="development_settings_title" msgid="140296922921597393">"ڈویلپر کے اختیارات"</string>
-    <string name="development_settings_enable" msgid="4285094651288242183">"ڈیولپر کے اختیارات فعال کریں"</string>
+    <string name="development_settings_enable" msgid="4285094651288242183">"ڈویلپر کے اختیارات فعال کریں"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"ایپ ڈویلپمنٹ کیلئے اختیارات سیٹ کریں"</string>
-    <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈیولپر کے اختیارات دستیاب نہیں ہیں"</string>
+    <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈویلپر کے اختیارات دستیاب نہیں ہیں"</string>
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"‏VPN ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"ٹیدرنگ ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"رسائی کی جگہ کے نام کی ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index 450bdb1..a0c8663 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -129,10 +129,14 @@
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "Found services for profile id " + profileId + ": " + resolveInfos);
         }
+
+        final PackageManager userPackageManager = mContext.createContextAsUser(
+                userHandle, /* flags */ 0).getPackageManager();
         List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size());
         for (ResolveInfo resolveInfo : resolveInfos) {
             try {
-                InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, pm);
+                InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle,
+                        userPackageManager);
                 if (setting == null) {
                     Log.w(TAG, "Unable to load service info " + resolveInfo);
                 } else {
@@ -248,8 +252,7 @@
                         + SettingInjectorService.ATTRIBUTES_NAME + " tag");
             }
 
-            Resources res = pm.getResourcesForApplicationAsUser(si.packageName,
-                    userHandle.getIdentifier());
+            Resources res = pm.getResourcesForApplication(si.packageName);
             return parseAttributes(si.packageName, si.name, userHandle, res, attrs);
         } catch (PackageManager.NameNotFoundException e) {
             throw new XmlPullParserException(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f1fb527..90bed12 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -749,25 +749,25 @@
                 Settings.Global.GPU_DEBUG_LAYERS,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE,
+                Settings.Global.ANGLE_DEBUG_PACKAGE,
                 GlobalSettingsProto.Gpu.ANGLE_DEBUG_PACKAGE);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_ALL_ANGLE);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_PKGS);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST,
+                Settings.Global.ANGLE_ALLOWLIST,
                 GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
         dumpSetting(s, p,
                 Settings.Global.ANGLE_EGL_FEATURES,
                 GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
+                Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX,
                 GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG);
         dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYER_APP,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9b0df96..4713243 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -408,6 +408,7 @@
                     Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
                     Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS,
                     Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
+                    Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE,
                     Settings.Global.POLICY_CONTROL,
                     Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
                     Settings.Global.POWER_MANAGER_CONSTANTS,
@@ -502,11 +503,11 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.GPU_DEBUG_LAYERS_GLES,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST,
+                    Settings.Global.ANGLE_DEBUG_PACKAGE,
+                    Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
+                    Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
+                    Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
+                    Settings.Global.ANGLE_ALLOWLIST,
                     Settings.Global.ANGLE_EGL_FEATURES,
                     Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
@@ -516,7 +517,7 @@
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST,
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST,
                     Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES,
-                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
+                    Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bf9b809..ef8064f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -573,10 +573,17 @@
         </activity>
 
         <!-- People Space UI Screen -->
-        <activity
-            android:name=".people.PeopleSpaceActivity"
+        <activity android:name=".people.PeopleSpaceActivity"
+            android:label="People"
+            android:icon="@drawable/ic_music_note"
             android:exported="true"
-            android:theme="@android:style/Theme.Material.NoActionBar">
+            android:enabled="false"
+            android:theme="@android:style/Theme.Material.NoActionBar"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
         </activity>
 
         <!-- a gallery of delicious treats -->
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index bf2963c..7e2e36a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -61,6 +61,28 @@
              android:visibility="invisible"
              />
     </FrameLayout>
+    <FrameLayout
+        android:id="@+id/new_lockscreen_clock_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:visibility="gone">
+        <com.android.keyguard.GradientTextClock
+            android:id="@+id/gradient_clock_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="100dp"
+            android:letterSpacing="0.02"
+            android:lineSpacingMultiplier=".8"
+            android:includeFontPadding="false"
+            android:fontFamily="sans-serif"
+            android:typeface="monospace"
+            android:format12Hour="hh\nmm"
+            android:format24Hour="HH\nmm"
+            android:elegantTextHeight="false"
+        />
+    </FrameLayout>
     <include layout="@layout/keyguard_status_area"
         android:id="@+id/keyguard_status_area"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index b50be52..e4d751a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Maak skermkiekie toe"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 9e8c8ac..8c22e8f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገጽ ዕይታን እንደገና ማንሳት ይሞክሩ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ቅጽበታዊ ገጽ እይታን አሰናብት"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index caae9fb..2c74a27 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"جرّب أخذ لقطة الشاشة مرة أخرى"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"إغلاق لقطة الشاشة"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
@@ -1087,6 +1089,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index f5f109e..94cd1a7 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"সঞ্চয়াগাৰত সীমিত খালী ঠাই থকাৰ বাবে স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এপটোৱে বা আপোনাৰ প্ৰতিষ্ঠানে স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি নিদিয়ে"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 89149f0..43aca9a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skrinşotu yenidən çəkin"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran şəklini ötürün"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index b3b722c9..3777dfe 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probajte da ponovo napravite snimak ekrana"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index f68b69c..8a5d42a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Паспрабуйце зрабіць здымак экрана яшчэ раз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчы"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Адхіліць здымак экрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 7a4b957..26a313a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Опитайте да направите екранна снимка отново"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Екранната снимка не може да се запази поради ограничено място в хранилището"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Правенето на екранни снимки не е разрешено от приложението или организацията ви"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Редактиране на екранната снимка"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отхвърляне на екранната снимка"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Текущата сесия не може да бъде скрита."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отхвърляне"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 0b5d65e..6cdc128 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্রিনশট বাতিল করুন"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বড় করে দেখার সুইচ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ফুল-স্ক্রিন মোডে দেখুন"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপনার কানেক্ট করা ডিভাইসের জন্য কন্ট্রোল যোগ করুন"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইস কন্ট্রোল সেট-আপ করুন"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 1d67443..8f622c8 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo snimiti ekran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 5e06f55..ea7f36a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prova de tornar a fer una captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora la captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1b03762..d3e3dbf 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zkuste snímek pořídit znovu"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavřít snímek obrazovky"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index fce888e..82e8fd1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv at tage et screenshot igen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller din organisation tillader ikke, at du tager screenshots"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Luk screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a6d2dfd..0e3f309 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
     <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
     <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
+    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
     <string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
     <string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
     <string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
     <string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
-    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
-    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
+    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
+    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
-    <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
+    <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
     <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
     <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
@@ -419,7 +421,7 @@
     <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
-    <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
+    <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +499,9 @@
     <string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
-    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
+    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
-    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
+    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +775,8 @@
       <item quantity="one">%d Minute</item>
     </plurals>
     <string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
-    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
-    <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
+    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
+    <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +963,11 @@
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
-    <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
+    <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
-    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
-    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
+    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
     <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index fc4e78b..645ff08 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Δοκιμάστε να κάνετε ξανά λήψη του στιγμιότυπου οθόνης"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Παράβλεψη στιγμιότυπου οθόνης"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 709a506..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 79a2844..3fe7a6a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 709a506..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 709a506..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index ab6699b..78006fd 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎Try taking screenshot again‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎Can\'t save screenshot due to limited storage space‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‎Taking screenshots isn\'t allowed by the app or your organization‎‏‎‎‏‎"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎Edit screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎Dismiss screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎Screenshot preview‎‏‎‎‏‎"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎Loading recommendations‎‏‎‎‏‎"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎Media‎‏‎‎‏‎"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎Hide the current session.‎‏‎‎‏‎"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎Current session cannot be hidden.‎‏‎‎‏‎"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎Dismiss‎‏‎‎‏‎"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎Resume‎‏‎‎‏‎"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎Settings‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index a1e21a1..2b5e96f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a hacer una captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La app o tu organización no permiten las capturas de pantalla"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Descartar captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 2003a7d..b0377fd 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cerrar captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 286a42b..a22e12b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Proovige ekraanipilt uuesti jäädvustada"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Sule ekraanipilt"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
@@ -1021,7 +1023,7 @@
     <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurenduse lüliti"</string>
     <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kogu ekraanikuva suurendamine"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
-    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Lüliti"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisage juhtelemendid ühendatud seadmete jaoks"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Seadmete juhtimisvidinate seadistamine"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b6beec0..f6ca697 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Saiatu berriro pantaila-argazkia ateratzen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ezin da gorde pantaila-argazkia ez delako gelditzen tokirik"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Baztertu pantaila-argazkia"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7849ea4..4c913f6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوباره نماگرفت بگیرید"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"به دلیل محدود بودن فضای ذخیره‌سازی نمی‌توان نماگرفت را ذخیره کرد"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"برنامه یا سازمان شما اجازه نمی‌دهند نماگرفت بگیرید."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"رد کردن نماگرفت"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"پیش‌نمایش نماگرفت"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ضبط‌کننده صفحه‌نمایش"</string>
@@ -142,7 +144,7 @@
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستی‌آزمایی ضربه بزنید"</string>
     <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"لطفاً دوباره امتحان کنید"</string>
     <string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"درحال جستجوی چهره"</string>
-    <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره احراز هویت شد"</string>
+    <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالت‌سنجی شد"</string>
     <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string>
     <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string>
     <string name="biometric_dialog_authenticated" msgid="7337147327545272484">"راستی‌آزمایی‌شده"</string>
@@ -307,8 +309,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفه‌جویی داده خاموش شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفه‌جویی داده روشن شد."</string>
-    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم خصوصی حسگر» خاموش است."</string>
-    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم خصوصی حسگر» روشن است."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم‌خصوصی حسگر» خاموش است."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم‌خصوصی حسگر» روشن است."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"روشنایی نمایشگر"</string>
     <string name="accessibility_ambient_display_charging" msgid="7725523068728128968">"درحال شارژ شدن"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5716594205739750015">"‏داده 2G-3G موقتاً متوقف شده است"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیه‌ها"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3e5afc7..0ab407a 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Yritä ottaa kuvakaappaus uudelleen."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hylkää kuvakaappaus"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 250183e..1b7a308 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de faire une autre capture d\'écran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2a4c95a..74a6861 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index fedbe2c..26cf07a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Volve tentar crear unha captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora a captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor do modo de ampliación"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplía toda a pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engade controis para os dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar o control de dispositivos"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d9c5b72..acaa442 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"મોટું કરવાની સુવિધાવાળી સ્વિચ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"સંપૂર્ણ સ્ક્રીન મોટી કરો"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"તમારા કનેક્ટ કરેલા ડિવાઇસ માટે નિયંત્રણો ઉમેરો"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ડિવાઇસનાં નિયંત્રણો સેટઅપ કરો"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 181ae74..d211dce 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट खारिज करें"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
@@ -1065,6 +1067,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f8318d5..b5671bf 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo napraviti snimku zaslona"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacivanje snimke zaslona"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
@@ -1026,7 +1028,7 @@
     <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prebacivanje povećavanja"</string>
     <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povećaj cijeli zaslon"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string>
-    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebaci"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodavanje kontrola za povezane uređaje"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavljanje kontrola uređaja"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 690508a..291976e 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Próbálja meg újra elkészíteni a képernyőképet"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nem menthet képernyőképet, mert kevés a tárhely"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Képernyőkép elvetése"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 0a966a1..1d80192 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Փորձեք նորից"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Չհաջողվեց պահել սքրինշոթը անբավարար հիշողության պատճառով"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում սքրինշոթի ստացումը"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Փոփոխել սքրինշոթը"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Փակել սքրինշոթը"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Ընթացիկ աշխատաշրջանը չի կարող թաքցվել։"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Փակել"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 750a38f..f219520 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Coba ambil screenshot lagi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Menutup screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 944e13f..8f1049f 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prófaðu að taka skjámynd aftur"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Loka skjámynd"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 68c64f0..a273ea8 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Riprova ad acquisire lo screenshot"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index e1ad35d..5ae10ad 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"יש לנסות שוב לבצע צילום מסך"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"לא היה מספיק מקום לשמור את צילום המסך"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"סגירת צילום מסך"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 763a80e..5d22c9a 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"スクリーンショットを撮り直してください"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"空き容量が足りないため、スクリーンショットを保存できません"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"スクリーンショットを閉じます"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 0086868..97aea29 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ხელახლა ცადეთ ეკრანის ანაბეჭდის გაკეთება"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ეკრანის ანაბეჭდის რედაქტირება"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ეკრანის ანაბეჭდის დახურვა"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"მიმდინარე სესიის დამალვა შეუძლებელია."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"დახურვა"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index f1c0121..68cd247 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Қайта скриншот жасап көріңіз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотты жабу"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 92d7cbf..55f3f44 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"សាកល្បង​ថតរូបថត​អេក្រង់​ម្តងទៀត"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"មិនអាច​រក្សាទុក​រូបថតអេក្រង់​បានទេ ​ដោយសារ​ទំហំផ្ទុក​មានកម្រិតទាប"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ការថត​រូបអេក្រង់​មិនត្រូវ​បាន​អនុញ្ញាត​ដោយ​កម្មវិធី​នេះ ឬ​ស្ថាប័ន​របស់អ្នក"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ច្រានចោល​រូបថត​អេក្រង់"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើល​រូបថត​អេក្រង់​សាកល្បង"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថត​អេក្រង់"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុក​ការណែនាំ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គ​បច្ចុប្បន្ន។"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រាន​ចោល"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2e47e9e..569837b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ಪುನಃ ತೆಗೆದುಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್‍ಶಾಟ್‍ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index fc533be..1fdff74 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"스크린샷을 다시 찍어 보세요."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"스크린샷 닫기"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a4e47b3..970d5fa 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Скриншотту кайра тартып көрүңүз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сактагычта бош орун аз болгондуктан, скриншот сакталбай жатат"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотту четке кагуу"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 78fdf09..9f0f0ae 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ກະລຸນາລອງຖ່າຍຮູບໜ້າຈໍອີກຄັ້ງ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຖ່າຍຮູບໜ້າຈໍ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ແກ້ໄຂຮູບໜ້າຈໍ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ປິດຮູບໜ້າຈໍ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອເຊດຊັນປັດຈຸບັນໄດ້."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6db8969..81696c2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pabandykite padaryti ekrano kopiją dar kartą"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Praleisti ekrano kopiją"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index c7807de..d37dd91 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Mēģiniet izveidot jaunu ekrānuzņēmumu."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Nerādīt ekrānuzņēmumu"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 203f6c2..28c4ee7 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Повторно обидете се да направите слика од екранот"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сликата од екранот не може да се зачува поради ограничена меморија"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отфрлете ја сликата од екранот"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 1076eac..f942641 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"സ്‌റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കാനാകുന്നില്ല"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"സ്ക്രീൻഷോട്ട് ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"സ്‌ക്രീൻഷോട്ട് പ്രിവ്യു"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്‌ക്കുക."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index c1ec99e..fc41f6c 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Дэлгэцийн зургийг дахин дарж үзнэ үү"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Дэлгэцийн агшныг хаах"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 40a51c7..2b01fef 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अ‍ॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
@@ -265,7 +267,7 @@
     <string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi बंद झाले."</string>
     <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi सुरू झाले."</string>
-    <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाईल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाइल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"बॅटरी <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"विमान मोड बंद."</string>
     <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"विमान मोड सुरू."</string>
@@ -298,8 +300,8 @@
     <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ्लॅशलाइट सुरू केला."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग उत्क्रमण बंद केले."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग उत्क्रमण सुरू केले."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाईल हॉटस्पॉट बंद केला."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाईल हॉटस्पॉट सुरू केला."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्पॉट बंद केला."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्पॉट सुरू केला."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रीन कास्ट करणे थांबले."</string>
     <string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"कार्य मोड बंद."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड सुरू."</string>
@@ -723,7 +725,7 @@
     <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"अलीकडील कोणतेही बबल नाहीत"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
-    <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
+    <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
     <string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"मॅग्निफिकेशन स्विच"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"संपूर्ण स्क्रीन मॅग्निफाय करा"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"तुमच्या कनेक्ट केलेल्या डिव्हाइससाठी नियंत्रणे जोडा"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"डिव्हाइस नियंत्रणे सेट करा"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index ac22f4c..5b0f91f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Cuba ambil tangkapan skrin sekali lagi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ketepikan tangkapan skrin"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index c92900c..b814607 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"မျက်နှာပြင်ပုံကို ထပ်ရိုက်ကြည့်ပါ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ဖန်သားပြင်ဓာတ်ပုံ ပယ်ရန်"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 2920af3..b676e91 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermdump på nytt"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Rediger skjermdumpen"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Avvis skjermdumpen"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Den nåværende økten kan ikke skjules."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Lukk"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6b77bb2..5eea7fd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
@@ -366,7 +368,7 @@
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
-    <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपतकालीन कल मात्र"</string>
+    <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्‌कालीन कल मात्र"</string>
     <string name="quick_settings_settings_label" msgid="2214639529565474534">"सेटिङहरू"</string>
     <string name="quick_settings_time_label" msgid="3352680970557509303">"समय"</string>
     <string name="quick_settings_user_label" msgid="1253515509432672496">"मलाई"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"म्याग्निफिकेसन स्विच"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरै स्क्रिन म्याग्निफाइ गर्नुहोस्"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"यन्त्र नियन्त्रण गर्ने विजेटहरू"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"आफ्ना जोडिएका यन्त्रहरूका लागि नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"यन्त्र नियन्त्रण गर्ने विजेटहरू सेटअप गर्नुहोस्"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b90cd30..2126b1a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer opnieuw een screenshot te maken"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot sluiten"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index fd7692a..b55fd67 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ପୁଣିଥରେ ସ୍କ୍ରୀନ୍‌ଶଟ୍ ନେବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ସୀମିତ ଷ୍ଟୋରେଜ୍‍ ସ୍ପେସ୍‍ ହେତୁ ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ ହୋଇପାରିବ ନାହିଁ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍‍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍‍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 19715c5..9714f34 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸਵਿੱਚ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ਸਾਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ਆਪਣੇ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਲਈ ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ਡੀਵਾਈਸ ਕੰਟਰੋਲਾਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 19cd0e9..f5bcdba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Spróbuj jeszcze raz wykonać zrzut ekranu"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zamknij zrzut ekranu"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index f8cf881..0db05d8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 0b1c5e3..147ed1d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Experimente voltar a efetuar a captura de ecrã."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A app ou a sua entidade não permitem tirar capturas de ecrã"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de ecrã"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignorar captura de ecrã"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index f8cf881..0db05d8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3502161..9c73e5a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Închideți captura de ecran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 7f257a1..b51a5b2 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Попробуйте сделать скриншот снова."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не удалось сохранить скриншот: недостаточно места."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрыть скриншот"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ac743d3..5d019e9 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"තිර රුව නැවත ගැනීමට උත්සාහ කරන්න"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"තිර රුව ඉවත ලන්න"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"මාධ්‍ය"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d29d1e9..88cc1a2 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skúste snímku urobiť znova"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavrieť snímku obrazovky"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 6cb0ccd..d1d14b2 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Poskusite znova ustvariti posnetek zaslona"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Opusti posnetek zaslona"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 154d53d..8d79aa4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hiq pamjen e ekranit"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ndërrimi i zmadhimit"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zmadho të gjithë ekranin"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Shto kontrolle për pajisjet e tua të lidhura"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguro kontrollet e pajisjes"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 687a1bd..5affcdf 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Пробајте да поново направите снимак екрана"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Чување снимка екрана није успело због ограниченог меморијског простора"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликација или организација не дозвољавају прављење снимака екрана"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Одбаците снимак екрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
@@ -1069,6 +1071,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3dc89d8..a51ed28 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Testa att ta en skärmdump igen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Stäng skärmdump"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmdump"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 76c1cd4..eab7f69 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Badilisha picha ya skrini"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ondoa picha ya skrini"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Huwezi kuficha kipindi cha sasa."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ondoa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 7e4ed35..3c1c12a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ஸ்கிரீன் ஷாட்டை மீண்டும் எடுக்க முயலவும்"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, ஆப்ஸ் அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ஸ்கிரீன்ஷாட்டை நிராகரி"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index f26c8b4..2680d22 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్‌షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్‌షాట్‌ను సేవ్ చేయడం సాధ్యపడదు"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్‌షాట్‌లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"స్క్రీన్‌షాట్‌ను మూసివేస్తుంది"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్‌షాట్ ప్రివ్యూ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్‌ను దాచు."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9c61e5e..2e4682b 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ลองบันทึกภาพหน้าจออีกครั้ง"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ปิดภาพหน้าจอ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string>
@@ -1018,7 +1020,7 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string>
-    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนการขยาย"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนโหมดการขยาย"</string>
     <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ขยายทั้งหน้าจอ"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index be7bd06..ae69c4f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Subukang kumuhang muli ng screenshot"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"I-dismiss ang screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 5d488365..0265176 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tekrar ekran görüntüsü almayı deneyin"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran görüntüsünü kapat"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3b5c44b..c9fd503 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Спробуйте зробити знімок екрана ще раз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрити знімок екрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string>
@@ -1075,6 +1077,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ffba2bd..1bfa853 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
@@ -1018,14 +1020,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string>
-    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
-    <skip />
-    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
-    <skip />
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"میگنیفکیشن پر سوئچ کریں"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"پوری اسکرین کو بڑا کریں"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"اپنے منسلک آلات کے لیے کنٹرولز شامل کریں"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"آلہ کے کنٹرولز سیٹ اپ کریں"</string>
@@ -1067,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 228b80d..332a26d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Qayta skrinshot olib ko‘ring"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Skrinshotni tahrirlash"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Skrinshotni yopish"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
@@ -1018,7 +1019,7 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string>
-    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirishni almashtirish"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirish rejimini almashtirish"</string>
     <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Butun ekranni kattalashtirish"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string>
@@ -1063,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seans berkilmaydi."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8492236..947bd43 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Hãy thử chụp lại màn hình"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Đóng ảnh chụp màn hình"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 72e7640..730fa81 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由于存储空间有限,无法保存屏幕截图"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"关闭屏幕截图"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index c3dc8d2..4236107 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再嘗試拍攝螢幕擷取畫面"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"應用程式或您的機構不允許擷取螢幕畫面"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 02fc24f..c7407ef 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再次嘗試拍攝螢幕截圖"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕截圖"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"這個應用程式或貴機構不允許擷取螢幕畫面"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8eb86bc..dd7f6f6 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -86,6 +86,8 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zama ukuthatha isithombe-skrini futhi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string>
+    <!-- no translation found for screenshot_edit (3510496440489019191) -->
+    <skip />
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cashisa isithombe-skrini"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
@@ -1063,6 +1065,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
+    <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+    <skip />
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b81ffb7..4238019 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -20,10 +20,6 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import android.annotation.NonNull;
 import android.app.Activity;
@@ -58,7 +54,6 @@
 
 import com.android.internal.app.IVoiceInteractionManagerService;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.ArrayList;
@@ -226,6 +221,22 @@
     public void startRecentsActivity(Intent intent, long eventTime,
             final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
+        boolean result = startRecentsActivity(intent, eventTime, animationHandler);
+        if (resultCallback != null) {
+            resultCallbackHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    resultCallback.accept(result);
+                }
+            });
+        }
+    }
+
+    /**
+     * Starts the recents activity. The caller should manage the thread on which this is called.
+     */
+    public boolean startRecentsActivity(
+            Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
         try {
             IRecentsAnimationRunner runner = null;
             if (animationHandler != null) {
@@ -257,23 +268,9 @@
                 };
             }
             ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner);
-            if (resultCallback != null) {
-                resultCallbackHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        resultCallback.accept(true);
-                    }
-                });
-            }
+            return true;
         } catch (Exception e) {
-            if (resultCallback != null) {
-                resultCallbackHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        resultCallback.accept(false);
-                    }
-                });
-            }
+            return false;
         }
     }
 
@@ -291,53 +288,17 @@
     /**
      * Starts a task from Recents.
      *
-     * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
-     */
-    public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
-            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
-        startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
-                ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
-    }
-
-    /**
-     * Starts a task from Recents.
-     *
      * @param resultCallback The result success callback
      * @param resultCallbackHandler The handler to receive the result callback
      */
-    public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options,
-            int windowingMode, int activityType, final Consumer<Boolean> resultCallback,
-            final Handler resultCallbackHandler) {
-        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            // We show non-visible docked tasks in Recents, but we always want to launch
-            // them in the fullscreen stack.
-            if (options == null) {
-                options = ActivityOptions.makeBasic();
-            }
-            options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        } else if (windowingMode != WINDOWING_MODE_UNDEFINED
-                || activityType != ACTIVITY_TYPE_UNDEFINED) {
-            if (options == null) {
-                options = ActivityOptions.makeBasic();
-            }
-            options.setLaunchWindowingMode(windowingMode);
-            options.setLaunchActivityType(activityType);
-        }
-        final ActivityOptions finalOptions = options;
-
-
-        boolean result = false;
-        try {
-            result = startActivityFromRecents(taskKey.id, finalOptions);
-        } catch (Exception e) {
-            // Fall through
-        }
-        final boolean finalResult = result;
+    public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
+            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
+        final boolean result = startActivityFromRecents(taskKey, options);
         if (resultCallback != null) {
             resultCallbackHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    resultCallback.accept(finalResult);
+                    resultCallback.accept(result);
                 }
             });
         }
@@ -346,6 +307,14 @@
     /**
      * Starts a task from Recents synchronously.
      */
+    public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) {
+        ActivityOptionsCompat.addTaskInfo(options, taskKey);
+        return startActivityFromRecents(taskKey.id, options);
+    }
+
+    /**
+     * Starts a task from Recents synchronously.
+     */
     public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
         try {
             Bundle optsBundle = options == null ? null : options.toBundle();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 0db4faf..1a71f11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.os.Handler;
 
+import com.android.systemui.shared.recents.model.Task;
+
 /**
  * Wrapper around internal ActivityOptions creation.
  */
@@ -94,4 +96,15 @@
         opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis);
         return opts;
     }
+
+    /**
+     * Sets Task specific information to the activity options
+     */
+    public static void addTaskInfo(ActivityOptions opts, Task.TaskKey taskKey) {
+        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            // We show non-visible docked tasks in Recents, but we always want to launch
+            // them in the fullscreen stack.
+            opts.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        }
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
deleted file mode 100644
index 7cd6c51..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 com.android.systemui.shared.system;
-
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/**
- * Abstract class for assist data receivers.
- */
-public abstract class AssistDataReceiver {
-    public void onHandleAssistData(Bundle resultData) {}
-    public void onHandleAssistScreenshot(Bitmap screenshot) {}
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
deleted file mode 100644
index 51fcb0a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 com.android.systemui.shared.system;
-
-import android.content.Context;
-
-/**
- * Wraps a context to expose some methods for launcher to call.
- */
-public class ContextCompat {
-    private final Context mWrapped;
-
-    public ContextCompat(Context context) {
-        mWrapped = context;
-    }
-
-    public int getUserId() {
-        return mWrapped.getUserId();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
new file mode 100644
index 0000000..b596388
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import android.content.Context;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.widget.TextClock;
+
+/**
+ * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
+ * The time's text color is a gradient that changes its colors based on its controller.
+ */
+public class GradientTextClock extends TextClock {
+    private int[] mGradientColors;
+    private float[] mPositions;
+
+    public GradientTextClock(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        addOnLayoutChangeListener(mOnLayoutChangeListener);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeOnLayoutChangeListener(mOnLayoutChangeListener);
+    }
+
+    @Override
+    public void refreshTime() {
+        updatePaint();
+        super.refreshTime();
+    }
+
+    @Override
+    public void setFormat12Hour(CharSequence format) {
+        super.setFormat12Hour(FORMAT_12);
+    }
+
+    @Override
+    public void setFormat24Hour(CharSequence format) {
+        super.setFormat24Hour(FORMAT_24);
+    }
+
+    public void setGradientColors(int[] colors) {
+        mGradientColors = colors;
+    }
+
+    public void setColorPositions(float[] positions) {
+        mPositions = positions;
+    }
+
+    private void updatePaint() {
+        getPaint().setShader(
+                new LinearGradient(
+                        getX(), getY(), getX(), getMeasuredHeight() + getY(),
+                        mGradientColors, mPositions, Shader.TileMode.REPEAT));
+    }
+
+    private final OnLayoutChangeListener mOnLayoutChangeListener =
+            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                if (bottom != oldBottom || top != oldTop) {
+                    updatePaint();
+                }
+            };
+
+    public static final CharSequence FORMAT_12 = "hh\nmm";
+    public static final CharSequence FORMAT_24 = "HH\nmm";
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 272954d..cf363cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -72,6 +72,16 @@
     private TextClock mClockViewBold;
 
     /**
+     * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
+     */
+    private TimeBasedColorsClockController mNewLockscreenClockViewController;
+
+    /**
+     * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
+     */
+    private FrameLayout mNewLockscreenClockFrame;
+
+    /**
      * Frame for default and custom clock.
      */
     private FrameLayout mSmallClockFrame;
@@ -137,23 +147,22 @@
         mLockScreenMode = mode;
         RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams)
                 mKeyguardStatusArea.getLayoutParams();
-        RelativeLayout.LayoutParams clockLP = (RelativeLayout.LayoutParams)
-                mSmallClockFrame.getLayoutParams();
 
         if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-            statusAreaLP.removeRule(RelativeLayout.BELOW);
-            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.clock_view);
-            statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
+            mSmallClockFrame.setVisibility(GONE);
+            mNewLockscreenClockFrame.setVisibility(VISIBLE);
+            mNewLockscreenClockViewController.init();
 
-            clockLP.addRule(RelativeLayout.ALIGN_PARENT_END);
-            clockLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+            statusAreaLP.removeRule(RelativeLayout.BELOW);
+            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view);
+            statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
         } else {
+            mSmallClockFrame.setVisibility(VISIBLE);
+            mNewLockscreenClockFrame.setVisibility(GONE);
+
             statusAreaLP.removeRule(RelativeLayout.LEFT_OF);
             statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
             statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
-
-            clockLP.removeRule(RelativeLayout.ALIGN_PARENT_END);
-            clockLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
         }
 
         requestLayout();
@@ -164,6 +173,9 @@
         super.onFinishInflate();
         mClockView = findViewById(R.id.default_clock_view);
         mClockViewBold = findViewById(R.id.default_clock_view_bold);
+        mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view);
+        mNewLockscreenClockViewController =
+                new TimeBasedColorsClockController(findViewById(R.id.gradient_clock_view));
         mSmallClockFrame = findViewById(R.id.clock_view);
         mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
@@ -336,6 +348,7 @@
      * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
      */
     public void refresh() {
+        mNewLockscreenClockViewController.refreshTime(System.currentTimeMillis());
         mClockView.refreshTime();
         mClockViewBold.refreshTime();
         if (mClockPlugin != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index a479bca..a9c06ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,9 +33,11 @@
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.View;
 import android.view.animation.Animation;
 import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.slice.SliceItem;
@@ -55,8 +57,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * View visible under the clock on the lock screen and AoD.
@@ -86,6 +90,8 @@
     private float mRowWithHeaderTextSize;
     private View.OnClickListener mOnClickListener;
 
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+
     public KeyguardSliceView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -142,6 +148,40 @@
         }
     }
 
+    /**
+     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
+     */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+            // add top padding to better align with top of clock
+            final int topPadding = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    20,
+                    getResources().getDisplayMetrics());
+            mTitle.setPaddingRelative(0, topPadding, 0, 0);
+            mTitle.setGravity(Gravity.START);
+            setGravity(Gravity.START);
+            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
+            lp.removeRule(RelativeLayout.CENTER_HORIZONTAL);
+            setLayoutParams(lp);
+        } else {
+            final int horizontalPaddingDpValue = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    44,
+                    getResources().getDisplayMetrics()
+            );
+            mTitle.setPaddingRelative(horizontalPaddingDpValue, 0, horizontalPaddingDpValue, 0);
+            mTitle.setGravity(Gravity.CENTER_HORIZONTAL);
+            setGravity(Gravity.CENTER_HORIZONTAL);
+            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
+            lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
+            setLayoutParams(lp);
+        }
+        mRow.setLockscreenMode(mode);
+        requestLayout();
+    }
+
     Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) {
         Trace.beginSection("KeyguardSliceView#showSlice");
         mHasHeader = header != null;
@@ -166,6 +206,8 @@
         final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
+        layoutParams.gravity = mLockScreenMode !=  KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL
+                ? Gravity.START :  Gravity.CENTER;
         layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
         mRow.setLayoutParams(layoutParams);
 
@@ -282,6 +324,7 @@
         pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         pw.println("  mDarkAmount: " + mDarkAmount);
         pw.println("  mHasHeader: " + mHasHeader);
+        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 
     @Override
@@ -291,6 +334,8 @@
     }
 
     public static class Row extends LinearLayout {
+        private Set<KeyguardSliceTextView> mKeyguardSliceTextViewSet = new HashSet();
+        private int mLockScreenModeRow = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         /**
          * This view is visible in AOD, which means that the device will sleep if we
@@ -361,12 +406,18 @@
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             int width = MeasureSpec.getSize(widthMeasureSpec);
             int childCount = getChildCount();
+
             for (int i = 0; i < childCount; i++) {
                 View child = getChildAt(i);
                 if (child instanceof KeyguardSliceTextView) {
-                    ((KeyguardSliceTextView) child).setMaxWidth(width / 3);
+                    if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                        ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE);
+                    } else {
+                        ((KeyguardSliceTextView) child).setMaxWidth(width / 3);
+                    }
                 }
             }
+
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
 
@@ -384,6 +435,42 @@
         public boolean hasOverlappingRendering() {
             return false;
         }
+
+        @Override
+        public void addView(View view, int index) {
+            super.addView(view, index);
+
+            if (view instanceof KeyguardSliceTextView) {
+                ((KeyguardSliceTextView) view).setLockScreenMode(mLockScreenModeRow);
+                mKeyguardSliceTextViewSet.add((KeyguardSliceTextView) view);
+            }
+        }
+
+        @Override
+        public void removeView(View view) {
+            super.removeView(view);
+            if (view instanceof KeyguardSliceTextView) {
+                mKeyguardSliceTextViewSet.remove((KeyguardSliceTextView) view);
+            }
+        }
+
+        /**
+         * Updates the lockscreen mode which may change the layout of this view.
+         */
+        public void setLockscreenMode(int mode) {
+            mLockScreenModeRow = mode;
+            if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                setOrientation(LinearLayout.VERTICAL);
+                setGravity(Gravity.START);
+            } else {
+                setOrientation(LinearLayout.HORIZONTAL);
+                setGravity(Gravity.CENTER);
+            }
+
+            for (KeyguardSliceTextView textView : mKeyguardSliceTextViewSet) {
+                textView.setLockScreenMode(mLockScreenModeRow);
+            }
+        }
     }
 
     /**
@@ -392,6 +479,7 @@
     @VisibleForTesting
     static class KeyguardSliceTextView extends TextView implements
             ConfigurationController.ConfigurationListener {
+        private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         @StyleRes
         private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
@@ -432,9 +520,16 @@
 
         private void updatePadding() {
             boolean hasText = !TextUtils.isEmpty(getText());
-            int horizontalPadding = (int) getContext().getResources()
+            int padding = (int) getContext().getResources()
                     .getDimension(R.dimen.widget_horizontal_padding) / 2;
-            setPadding(horizontalPadding, 0, horizontalPadding * (hasText ? 1 : -1), 0);
+            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                // orientation is vertical, so add padding to top & bottom
+                setPadding(0, padding, 0, padding * (hasText ? 1 : -1));
+            } else {
+                // oreintation is horizontal, so add padding to left & right
+                setPadding(padding, 0, padding * (hasText ? 1 : -1), 0);
+            }
+
             setCompoundDrawablePadding((int) mContext.getResources()
                     .getDimension(R.dimen.widget_icon_padding));
         }
@@ -461,5 +556,18 @@
                 }
             }
         }
+
+        /**
+         * Updates the lockscreen mode which may change the layout of this view.
+         */
+        public void setLockScreenMode(int mode) {
+            mLockScreenMode = mode;
+            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                setGravity(Gravity.START);
+            } else {
+                setGravity(Gravity.CENTER);
+            }
+            updatePadding();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 3be7e0a..8b55b06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -55,10 +56,10 @@
 
 /** Controller for a {@link KeyguardSliceView}. */
 @KeyguardStatusViewScope
-public class KeyguardSliceViewController implements Dumpable {
+public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements
+        Dumpable {
     private static final String TAG = "KeyguardSliceViewCtrl";
 
-    private final KeyguardSliceView mView;
     private final ActivityStarter mActivityStarter;
     private final ConfigurationController mConfigurationController;
     private final TunerService mTunerService;
@@ -68,41 +69,7 @@
     private Uri mKeyguardSliceUri;
     private Slice mSlice;
     private Map<View, PendingIntent> mClickActions;
-
-    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
-            new View.OnAttachStateChangeListener() {
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-
-                    Display display = mView.getDisplay();
-                    if (display != null) {
-                        mDisplayId = display.getDisplayId();
-                    }
-                    mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
-                    // Make sure we always have the most current slice
-                    if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
-                        mLiveData.observeForever(mObserver);
-                    }
-                    mConfigurationController.addCallback(mConfigurationListener);
-                    mDumpManager.registerDumpable(
-                            TAG + "@" + Integer.toHexString(
-                                    KeyguardSliceViewController.this.hashCode()),
-                            KeyguardSliceViewController.this);
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-
-                    // TODO(b/117344873) Remove below work around after this issue be fixed.
-                    if (mDisplayId == DEFAULT_DISPLAY) {
-                        mLiveData.removeObserver(mObserver);
-                    }
-                    mTunerService.removeTunable(mTunable);
-                    mConfigurationController.removeCallback(mConfigurationListener);
-                    mDumpManager.unregisterDumpable(TAG);
-                }
-            };
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
 
@@ -133,24 +100,57 @@
     };
 
     @Inject
-    public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView,
+    public KeyguardSliceViewController(
+            KeyguardSliceView keyguardSliceView,
             ActivityStarter activityStarter,
-            ConfigurationController configurationController, TunerService tunerService,
+            ConfigurationController configurationController,
+            TunerService tunerService,
             DumpManager dumpManager) {
-        mView = keyguardSliceView;
+        super(keyguardSliceView);
         mActivityStarter = activityStarter;
         mConfigurationController = configurationController;
         mTunerService = tunerService;
         mDumpManager = dumpManager;
     }
 
-    /** Initialize the controller. */
-    public void init() {
-        if (mView.isAttachedToWindow()) {
-            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
+    @Override
+    protected void onViewAttached() {
+        Display display = mView.getDisplay();
+        if (display != null) {
+            mDisplayId = display.getDisplayId();
         }
-        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        mView.setOnClickListener(mOnClickListener);
+        mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
+        // Make sure we always have the most current slice
+        if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
+            mLiveData.observeForever(mObserver);
+        }
+        mConfigurationController.addCallback(mConfigurationListener);
+        mDumpManager.registerDumpable(
+                TAG + "@" + Integer.toHexString(
+                        KeyguardSliceViewController.this.hashCode()),
+                KeyguardSliceViewController.this);
+        mView.updateLockScreenMode(mLockScreenMode);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        // TODO(b/117344873) Remove below work around after this issue be fixed.
+        if (mDisplayId == DEFAULT_DISPLAY) {
+            mLiveData.removeObserver(mObserver);
+        }
+        mTunerService.removeTunable(mTunable);
+        mConfigurationController.removeCallback(mConfigurationListener);
+        mDumpManager.unregisterDumpable(
+                TAG + "@" + Integer.toHexString(
+                        KeyguardSliceViewController.this.hashCode()));
+    }
+
+    /**
+     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
+     */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        mView.updateLockScreenMode(mLockScreenMode);
     }
 
     /**
@@ -224,10 +224,10 @@
         Trace.endSection();
     }
 
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mSlice: " + mSlice);
         pw.println("  mClickActions: " + mClickActions);
+        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 0efb5ee..7705db4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -267,6 +267,7 @@
         @Override
         public void onLockScreenModeChanged(int mode) {
             mKeyguardClockSwitchController.updateLockScreenMode(mode);
+            mKeyguardSliceViewController.updateLockScreenMode(mode);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
new file mode 100644
index 0000000..4f1963e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import com.android.systemui.util.ViewController;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * Changes the color of the text clock based on the time of day.
+ */
+public class TimeBasedColorsClockController extends ViewController<GradientTextClock> {
+    private final int[] mGradientColors = new int[3];
+    private final float[] mPositions = new float[3];
+
+    public TimeBasedColorsClockController(GradientTextClock view) {
+        super(view);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        refreshTime(System.currentTimeMillis());
+    }
+
+    @Override
+    protected void onViewDetached() {
+
+    }
+
+    /**
+     * Updates the time for this view. Also updates any color changes.
+     */
+    public void refreshTime(long timeInMillis) {
+        Calendar now = new GregorianCalendar();
+        now.setTimeInMillis(timeInMillis);
+        updateColors(now);
+        updatePositions(now);
+        mView.refreshTime();
+    }
+
+    private int getTimeIndex(Calendar now) {
+        int hour = now.get(Calendar.HOUR_OF_DAY); // 0 - 23
+        if (hour < mTimes[0]) {
+            return mTimes.length - 1;
+        }
+
+        for (int i = 1; i < mTimes.length; i++) {
+            if (hour < mTimes[i]) {
+                return i - 1;
+            }
+        }
+
+        return mTimes.length - 1;
+    }
+
+    private void updateColors(Calendar now) {
+        final int index = getTimeIndex(now);
+        for (int i = 0; i < mGradientColors.length; i++) {
+            mGradientColors[i] = COLORS[index][i];
+        }
+        mView.setGradientColors(mGradientColors);
+    }
+
+    private void updatePositions(Calendar now) {
+        final int index = getTimeIndex(now);
+
+        final Calendar startTime = new GregorianCalendar();
+        startTime.setTimeInMillis(now.getTimeInMillis());
+        startTime.set(Calendar.HOUR_OF_DAY, mTimes[index]);
+        if (startTime.getTimeInMillis() > now.getTimeInMillis()) {
+            // start should be earlier than 'now'
+            startTime.add(Calendar.DATE, -1);
+        }
+
+        final Calendar endTime = new GregorianCalendar();
+        endTime.setTimeInMillis(now.getTimeInMillis());
+        if (index == mTimes.length - 1) {
+            endTime.set(Calendar.HOUR_OF_DAY, mTimes[0]);
+            endTime.add(Calendar.DATE, 1); // end time is tomorrow
+        } else {
+            endTime.set(Calendar.HOUR_OF_DAY, mTimes[index + 1]);
+        }
+
+        long totalTimeInThisColorGradient = endTime.getTimeInMillis() - startTime.getTimeInMillis();
+        long timeIntoThisColorGradient = now.getTimeInMillis() - startTime.getTimeInMillis();
+        float percentageWithinGradient =
+                (float) timeIntoThisColorGradient / (float) totalTimeInThisColorGradient;
+
+        for (int i = 0; i < mPositions.length; i++) {
+            // currently hard-coded .3 movement of gradient
+            mPositions[i] = POSITIONS[index][i] - (.3f * percentageWithinGradient);
+        }
+        mView.setColorPositions(mPositions);
+    }
+
+    private static final int[] SUNRISE = new int[] {0xFF6F75AA, 0xFFAFF0FF, 0xFFFFDEBF};
+    private static final int[] DAY = new int[] {0xFF9BD8FB, 0xFFD7F5FF, 0xFFFFF278};
+    private static final int[] NIGHT = new int[] {0xFF333D5E, 0xFFC5A1D6, 0xFF907359};
+
+    private static final float[] SUNRISE_START_POSITIONS = new float[] {.3f, .5f, .8f};
+    private static final float[] DAY_START_POSITIONS = new float[] {.4f, .8f, 1f};
+    private static final float[] NIGHT_START_POSITIONS = new float[] {.25f, .5f, .8f};
+
+    // TODO (b/170228350): use TwilightManager to set sunrise/sunset times
+    private final int mSunriseTime = 6; // 6am
+    private final int mDaytime = 9; // 9 am
+    private final int mNightTime = 19; // 7pm
+
+    private int[] mTimes = new int[] {
+            mSunriseTime,
+            mDaytime,
+            mNightTime
+    };
+    private static final int[][] COLORS = new int[][] {
+            SUNRISE,
+            DAY,
+            NIGHT
+    };
+    private static final float[][] POSITIONS = new float[][] {
+            SUNRISE_START_POSITIONS,
+            DAY_START_POSITIONS,
+            NIGHT_START_POSITIONS
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9f28e09..8ac6edb 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -119,13 +119,11 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
 import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -340,12 +338,10 @@
     @Inject Lazy<CommandQueue> mCommandQueue;
     @Inject Lazy<Recents> mRecents;
     @Inject Lazy<StatusBar> mStatusBar;
-    @Inject Lazy<DisplayController> mDisplayController;
-    @Inject Lazy<SystemWindows> mSystemWindows;
-    @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
     @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
+    @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
 
     @Inject
     public Dependency() {
@@ -530,10 +526,8 @@
         mProviders.put(CommandQueue.class, mCommandQueue::get);
         mProviders.put(Recents.class, mRecents::get);
         mProviders.put(StatusBar.class, mStatusBar::get);
-        mProviders.put(DisplayController.class, mDisplayController::get);
-        mProviders.put(SystemWindows.class, mSystemWindows::get);
-        mProviders.put(DisplayImeController.class, mDisplayImeController::get);
         mProviders.put(ProtoTracer.class, mProtoTracer::get);
+        mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
 
         // TODO(b/118592525): to support multi-display , we start to add something which is
         //                    per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 7dcec3d..b388a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -19,15 +19,18 @@
 import android.app.ActivityThread;
 import android.app.Application;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 
@@ -35,6 +38,7 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.util.NotificationChannels;
 
 import java.lang.reflect.Constructor;
@@ -104,6 +108,19 @@
                             mServices[i].onBootCompleted();
                         }
                     }
+                    // If flag SHOW_PEOPLE_SPACE is true, enable People Space launcher icon.
+                    try {
+                        int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.SHOW_PEOPLE_SPACE);
+                        context.getPackageManager().setComponentEnabledSetting(
+                                new ComponentName(context, PeopleSpaceActivity.class),
+                                showPeopleSpace == 1
+                                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                                PackageManager.DONT_KILL_APP);
+                    } catch (Exception e) {
+                        Log.w(TAG, "Error enabling People Space launch icon:", e);
+                    }
                 }
             }, bootCompletedFilter);
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 80253b4..4814501 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
 import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
 
+import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
@@ -49,6 +50,11 @@
     }
 
     public static void createFromConfig(Context context) {
+        createFromConfig(context, false);
+    }
+
+    @VisibleForTesting
+    public static void createFromConfig(Context context, boolean fromTest) {
         if (mFactory != null) {
             return;
         }
@@ -62,7 +68,7 @@
             Class<?> cls = null;
             cls = context.getClassLoader().loadClass(clsName);
             mFactory = (SystemUIFactory) cls.newInstance();
-            mFactory.init(context);
+            mFactory.init(context, fromTest);
         } catch (Throwable t) {
             Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
             throw new RuntimeException(t);
@@ -76,16 +82,38 @@
 
     public SystemUIFactory() {}
 
-    private void init(Context context) throws ExecutionException, InterruptedException {
+    @VisibleForTesting
+    public void init(Context context, boolean fromTest)
+            throws ExecutionException, InterruptedException {
         mRootComponent = buildGlobalRootComponent(context);
         // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
+        if (!fromTest) {
+            // Only initialize when not starting from tests since this currently initializes some
+            // components that shouldn't be run in the test environment
+            mWMComponent.init();
+        }
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
-        // TODO: StubAPIClass is just a placeholder.
-        mSysUIComponent = mRootComponent.getSysUIComponent()
-                .setStubAPIClass(mWMComponent.createStubAPIClass())
+        SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
+        if (!fromTest) {
+            // Only initialize when not starting from tests since this currently initializes some
+            // components that shouldn't be run in the test environment
+            builder = builder.setPip(mWMComponent.getPip())
+                    .setSplitScreen(mWMComponent.getSplitScreen())
+                    .setOneHanded(mWMComponent.getOneHanded());
+        } else {
+            builder = builder.setPip(Optional.ofNullable(null))
+                    .setSplitScreen(Optional.ofNullable(null))
+                    .setOneHanded(Optional.ofNullable(null));
+        }
+        mSysUIComponent = builder
+                .setInputConsumerController(mWMComponent.getInputConsumerController())
+                .setShellTaskOrganizer(mWMComponent.getShellTaskOrganizer())
                 .build();
+        if (!fromTest) {
+            mSysUIComponent.init();
+        }
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
@@ -93,6 +121,16 @@
         dependency.start();
     }
 
+    /**
+     * Prepares the SysUIComponent builder before it is built.
+     * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
+     * @param wm the built WMComponent from the root component's getWMComponent() method
+     */
+    protected SysUIComponent.Builder prepareSysUIComponentBuilder(
+            SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
+        return sysUIBuilder;
+    }
+
     protected GlobalRootComponent buildGlobalRootComponent(Context context) {
         return DaggerGlobalRootComponent.builder()
                 .context(context)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 3f94b00..0c3dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -177,6 +177,7 @@
     private ScrimView mBubbleScrim;
     @Nullable private BubbleStackView mStackView;
     private BubbleIconFactory mBubbleIconFactory;
+    private BubblePositioner mBubblePositioner;
 
     /**
      * The relative position of the stack when we removed it and nulled it out. If the stack is
@@ -244,7 +245,7 @@
 
     private boolean mInflateSynchronously;
 
-    private MultiWindowTaskListener mTaskListener;
+    private ShellTaskOrganizer mTaskOrganizer;
 
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
@@ -387,7 +388,7 @@
                 dumpManager, floatingContentCoordinator,
                 new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager,
                 statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger,
-                mainHandler, organizer);
+                mainHandler, organizer, new BubblePositioner(context, windowManager));
     }
 
     /**
@@ -419,7 +420,8 @@
             LauncherApps launcherApps,
             BubbleLogger bubbleLogger,
             Handler mainHandler,
-            ShellTaskOrganizer organizer) {
+            ShellTaskOrganizer organizer,
+            BubblePositioner positioner) {
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
@@ -529,7 +531,8 @@
                 });
 
         mBubbleIconFactory = new BubbleIconFactory(context);
-        mTaskListener = new MultiWindowTaskListener(mMainHandler, organizer);
+        mTaskOrganizer = organizer;
+        mBubblePositioner = positioner;
 
         launcherApps.registerCallback(new LauncherApps.Callback() {
             @Override
@@ -805,8 +808,13 @@
     }
 
     @Override
-    public MultiWindowTaskListener getTaskManager() {
-        return mTaskListener;
+    public ShellTaskOrganizer getTaskOrganizer() {
+        return mTaskOrganizer;
+    }
+
+    @Override
+    public BubblePositioner getPositioner() {
+        return mBubblePositioner;
     }
 
     /**
@@ -818,9 +826,10 @@
             mStackView = new BubbleStackView(
                     mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
                     this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
-                    this::hideCurrentInputMethod, this::onBubbleExpandChanged);
+                    this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner);
             mStackView.setStackStartPosition(mPositionFromRemovedStack);
             mStackView.addView(mBubbleScrim);
+            mStackView.onOrientationChanged();
             if (mExpandListener != null) {
                 mStackView.setExpandListener(mExpandListener);
             }
@@ -978,10 +987,14 @@
 
     @Override
     public void onConfigChanged(Configuration newConfig) {
+        if (mBubblePositioner != null) {
+            // This doesn't trigger any changes, always update it
+            mBubblePositioner.update(newConfig.orientation);
+        }
         if (mStackView != null && newConfig != null) {
             if (newConfig.orientation != mOrientation) {
                 mOrientation = newConfig.orientation;
-                mStackView.onOrientationChanged(newConfig.orientation);
+                mStackView.onOrientationChanged();
             }
             if (newConfig.densityDpi != mDensityDpi) {
                 mDensityDpi = newConfig.densityDpi;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 98a2257..bc06020 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -38,7 +38,6 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Outline;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.Bundle;
@@ -48,7 +47,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -60,6 +58,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
 import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.wm.shell.common.HandlerExecutor;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -83,24 +82,26 @@
     private boolean mImeVisible;
     private boolean mNeedsNewHeight;
 
-    private Point mDisplaySize;
     private int mMinHeight;
     private int mOverflowHeight;
     private int mSettingsIconHeight;
     private int mPointerWidth;
     private int mPointerHeight;
-    private ShapeDrawable mPointerDrawable;
+    private ShapeDrawable mCurrentPointer;
+    private ShapeDrawable mTopPointer;
+    private ShapeDrawable mLeftPointer;
+    private ShapeDrawable mRightPointer;
     private int mExpandedViewPadding;
     private float mCornerRadius = 0f;
 
     @Nullable private Bubble mBubble;
     private PendingIntent mPendingIntent;
-
+    // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead
     private boolean mIsOverflow;
 
     private Bubbles mBubbles = Dependency.get(Bubbles.class);
-    private WindowManager mWindowManager;
     private BubbleStackView mStackView;
+    private BubblePositioner mPositioner;
 
     /**
      * Container for the ActivityView that has a solid, round-rect background that shows if the
@@ -136,6 +137,7 @@
                 }
                 try {
                     if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+                        options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
                                 options, null /* sourceBounds */);
                     } else {
@@ -224,17 +226,6 @@
         updateDimensions();
     }
 
-    void updateDimensions() {
-        mDisplaySize = new Point();
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
-        mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
-        Resources res = getResources();
-        mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
-        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
-        mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
-    }
-
     @SuppressLint("ClickableViewAccessibility")
     @Override
     protected void onFinishInflate() {
@@ -245,15 +236,24 @@
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
 
-        mPointerDrawable = new ShapeDrawable(TriangleShape.create(
+        mTopPointer = new ShapeDrawable(TriangleShape.create(
                 mPointerWidth, mPointerHeight, true /* pointUp */));
+        mLeftPointer = new ShapeDrawable(TriangleShape.createHorizontal(
+                mPointerWidth, mPointerHeight, true /* pointLeft */));
+        mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal(
+                mPointerWidth, mPointerHeight, false /* pointLeft */));
+
+        mCurrentPointer = mTopPointer;
         mPointerView.setVisibility(INVISIBLE);
 
         mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_manage_button_height);
         mSettingsIcon = findViewById(R.id.settings_button);
 
-        mTaskView = new TaskView(mContext, mBubbles.getTaskManager());
+        mPositioner = mBubbles.getPositioner();
+
+        mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer(),
+                new HandlerExecutor(getHandler()));
         // Set ActivityView's alpha value as zero, since there is no view content to be shown.
         setContentVisibility(false);
 
@@ -282,8 +282,7 @@
         applyThemeAttrs();
 
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
-        setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding,
-                mExpandedViewPadding);
+        setClipToPadding(false);
         setOnTouchListener((view, motionEvent) -> {
             if (mTaskView == null) {
                 return false;
@@ -311,6 +310,52 @@
         setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
     }
 
+    void updateDimensions() {
+        Resources res = getResources();
+        mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
+        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
+        mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
+    }
+
+    void applyThemeAttrs() {
+        final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+                android.R.attr.dialogCornerRadius,
+                android.R.attr.colorBackgroundFloating});
+        mCornerRadius = ta.getDimensionPixelSize(0, 0);
+        mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
+        ta.recycle();
+
+        if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+                mContext.getResources())) {
+            mTaskView.setCornerRadius(mCornerRadius);
+        }
+        updatePointerView();
+    }
+
+    private void updatePointerView() {
+        final int mode =
+                getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        switch (mode) {
+            case Configuration.UI_MODE_NIGHT_NO:
+                mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_light));
+                break;
+            case Configuration.UI_MODE_NIGHT_YES:
+                mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_dark));
+                break;
+        }
+        LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams();
+        if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) {
+            lp.width = mPointerHeight;
+            lp.height = mPointerWidth;
+        } else {
+            lp.width = mPointerWidth;
+            lp.height = mPointerHeight;
+        }
+        mPointerView.setLayoutParams(lp);
+        mPointerView.setBackground(mCurrentPointer);
+    }
+
+
     private String getBubbleKey() {
         return mBubble != null ? mBubble.getKey() : "null";
     }
@@ -371,32 +416,6 @@
         }
     }
 
-    void applyThemeAttrs() {
-        final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
-                android.R.attr.dialogCornerRadius,
-                android.R.attr.colorBackgroundFloating});
-        mCornerRadius = ta.getDimensionPixelSize(0, 0);
-        mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
-        ta.recycle();
-
-        if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
-                mContext.getResources())) {
-            mTaskView.setCornerRadius(mCornerRadius);
-        }
-
-        final int mode =
-                getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        switch (mode) {
-            case Configuration.UI_MODE_NIGHT_NO:
-                mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_light));
-                break;
-            case Configuration.UI_MODE_NIGHT_YES:
-                mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_dark));
-                break;
-        }
-        mPointerView.setBackground(mPointerDrawable);
-    }
-
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
@@ -522,12 +541,12 @@
         }
 
         if (mBubble != null || mIsOverflow) {
-            float desiredHeight = mOverflowHeight;
-            if (!mIsOverflow) {
-                desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
-            }
+            float desiredHeight = mIsOverflow
+                    ? mOverflowHeight
+                    : mBubble.getDesiredHeight(mContext);
+            desiredHeight = Math.max(desiredHeight, mMinHeight);
             float height = Math.min(desiredHeight, getMaxExpandedHeight());
-            height = Math.max(height, mIsOverflow ? mOverflowHeight : mMinHeight);
+            height = Math.max(height, mMinHeight);
             FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
             mNeedsNewHeight = lp.height != height;
             if (!mImeVisible) {
@@ -546,21 +565,17 @@
     }
 
     private int getMaxExpandedHeight() {
-        mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
         int expandedContainerY = mExpandedViewContainerLocation != null
-                ? mExpandedViewContainerLocation[1]
+                // Remove top insets back here because availableRect.height would account for that
+                ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top
                 : 0;
-        int bottomInset = getRootWindowInsets() != null
-                ? getRootWindowInsets().getStableInsetBottom()
-                : 0;
-
-        return mDisplaySize.y
+        return mPositioner.getAvailableRect().height()
                 - expandedContainerY
                 - getPaddingTop()
                 - getPaddingBottom()
                 - mSettingsIconHeight
                 - mPointerHeight
-                - mPointerMargin - bottomInset;
+                - mPointerMargin;
     }
 
     /**
@@ -585,12 +600,25 @@
     }
 
     /**
-     * Set the x position that the tip of the triangle should point to.
+     * Set the position that the tip of the triangle should point to.
      */
-    public void setPointerPosition(float x) {
-        float halfPointerWidth = mPointerWidth / 2f;
-        float pointerLeft = x - halfPointerWidth - mExpandedViewPadding;
-        mPointerView.setTranslationX(pointerLeft);
+    public void setPointerPosition(float x, float y, boolean isLandscape, boolean onLeft) {
+        // Pointer gets drawn in the padding
+        int paddingLeft = (isLandscape && onLeft) ? mPointerHeight : 0;
+        int paddingRight = (isLandscape && !onLeft) ? mPointerHeight : 0;
+        int paddingTop = isLandscape ? 0 : mExpandedViewPadding;
+        setPadding(paddingLeft, paddingTop, paddingRight, 0);
+
+        if (isLandscape) {
+            // TODO: why setY vs setTranslationY ? linearlayout?
+            mPointerView.setY(y - (mPointerWidth / 2f));
+            mPointerView.setTranslationX(onLeft ? -mPointerHeight : x - mExpandedViewPadding);
+        } else {
+            mPointerView.setTranslationY(0f);
+            mPointerView.setTranslationX(x - mExpandedViewPadding - (mPointerWidth / 2f));
+        }
+        mCurrentPointer = isLandscape ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
+        updatePointerView();
         mPointerView.setVisibility(VISIBLE);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
new file mode 100644
index 0000000..029caee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Keeps track of display size, configuration, and specific bubble sizes. One place for all
+ * placement and positioning calculations to refer to.
+ */
+public class BubblePositioner {
+
+    private WindowManager mWindowManager;
+    private Rect mPositionRect;
+    private int mOrientation;
+    private Insets mInsets;
+
+    public BubblePositioner(Context context, WindowManager windowManager) {
+        mWindowManager = windowManager;
+        update(Configuration.ORIENTATION_UNDEFINED);
+    }
+
+    public void update(int orientation) {
+        WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+        mPositionRect = new Rect(windowMetrics.getBounds());
+        WindowInsets metricInsets = windowMetrics.getWindowInsets();
+
+        Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+                | WindowInsets.Type.statusBars()
+                | WindowInsets.Type.displayCutout());
+        update(orientation, insets, windowMetrics.getBounds());
+    }
+
+    @VisibleForTesting
+    public void update(int orientation, Insets insets, Rect bounds) {
+        mOrientation = orientation;
+        mInsets = insets;
+
+        mPositionRect = new Rect(bounds);
+        mPositionRect.left += mInsets.left;
+        mPositionRect.top += mInsets.top;
+        mPositionRect.right -= mInsets.right;
+        mPositionRect.bottom -= mInsets.bottom;
+    }
+
+    /**
+     * @return a rect of available screen space for displaying bubbles in the correct orientation,
+     * accounting for system bars and cutouts.
+     */
+    public Rect getAvailableRect() {
+        return mPositionRect;
+    }
+
+    /**
+     * @return the current orientation.
+     */
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * @return the relevant insets (status bar, nav bar, cutouts).
+     */
+    public Insets getInsets() {
+        return mInsets;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 431719f..0714c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -35,9 +35,9 @@
 import android.content.res.TypedArray;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Insets;
 import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -47,7 +47,6 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Choreographer;
-import android.view.DisplayCutout;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -57,7 +56,6 @@
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -188,8 +186,6 @@
         }
     };
 
-    private Point mDisplaySize;
-
     private final BubbleData mBubbleData;
 
     private final ValueAnimator mDesaturateAndDarkenAnimator;
@@ -245,8 +241,8 @@
     private int mBubblePaddingTop;
     private int mBubbleTouchPadding;
     private int mExpandedViewPadding;
+    private int mPointerHeight;
     private int mCornerRadius;
-    private int mStatusBarHeight;
     private int mImeOffset;
     @Nullable private BubbleViewProvider mExpandedBubble;
     private boolean mIsExpanded;
@@ -721,13 +717,11 @@
         }
     };
 
-    private DismissView mDismissView;
-    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-
     @Nullable
     private BubbleOverflow mBubbleOverflow;
     private StackEducationView mStackEduView;
     private ManageEducationView mManageEduView;
+    private DismissView mDismissView;
 
     private ViewGroup mManageMenu;
     private ImageView mManageSettingsIcon;
@@ -735,6 +729,8 @@
     private boolean mShowingManage = false;
     private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
             SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+    private BubblePositioner mPositioner;
+
     @SuppressLint("ClickableViewAccessibility")
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer,
@@ -742,7 +738,8 @@
             Runnable allBubblesAnimatedOutAction,
             Consumer<Boolean> onImeVisibilityChanged,
             Runnable hideCurrentInputMethodCallback,
-            Consumer<Boolean> onBubbleExpandChanged) {
+            Consumer<Boolean> onBubbleExpandChanged,
+            BubblePositioner positioner) {
         super(context);
 
         mBubbleData = data;
@@ -753,15 +750,11 @@
         mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
+        mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
 
-        mStatusBarHeight =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
-        mDisplaySize = new Point();
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        // We use the real size & subtract screen decorations / window insets ourselves when needed
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
+        mPositioner = positioner;
 
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -778,11 +771,10 @@
         };
 
         mStackAnimationController = new StackAnimationController(
-                floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut);
+                floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner);
 
         mExpandedAnimationController = new ExpandedAnimationController(
-                mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation,
-                onBubbleAnimatedOut);
+                mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
         mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
 
         // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -817,10 +809,10 @@
         mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView);
 
         mAnimatingOutSurfaceContainer.setPadding(
-                mExpandedViewPadding,
-                mExpandedViewPadding,
-                mExpandedViewPadding,
-                mExpandedViewPadding);
+                mExpandedViewContainer.getPaddingLeft(),
+                mExpandedViewContainer.getPaddingTop(),
+                mExpandedViewContainer.getPaddingRight(),
+                mExpandedViewContainer.getPaddingBottom());
 
         setUpManageMenu();
 
@@ -871,29 +863,16 @@
 
         mOrientationChangedListener =
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
-                    mStackAnimationController.updateResources(mOrientation);
+                    onDisplaySizeChanged();
+                    mExpandedAnimationController.updateResources();
+                    mStackAnimationController.updateResources();
                     mBubbleOverflow.updateResources();
 
-                    // Need to update the padding around the view
-                    WindowInsets insets = getRootWindowInsets();
-                    int leftPadding = mExpandedViewPadding;
-                    int rightPadding = mExpandedViewPadding;
-                    if (insets != null) {
-                        // Can't have the expanded view overlaying notches
-                        int cutoutLeft = 0;
-                        int cutoutRight = 0;
-                        DisplayCutout cutout = insets.getDisplayCutout();
-                        if (cutout != null) {
-                            cutoutLeft = cutout.getSafeInsetLeft();
-                            cutoutRight = cutout.getSafeInsetRight();
-                        }
-                        // Or overlaying nav or status bar
-                        leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft());
-                        rightPadding += Math.max(cutoutRight, insets.getStableInsetRight());
+                    if (mRelativeStackPositionBeforeRotation != null) {
+                        mStackAnimationController.setStackPosition(
+                                mRelativeStackPositionBeforeRotation);
+                        mRelativeStackPositionBeforeRotation = null;
                     }
-                    mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding,
-                            rightPadding, mExpandedViewPadding);
 
                     if (mIsExpanded) {
                         // Re-draw bubble row and pointer for new orientation.
@@ -903,15 +882,10 @@
                         mExpandedAnimationController.expandFromStack(() -> {
                             afterExpandedViewAnimation();
                         } /* after */);
-                        mExpandedViewContainer.setTranslationX(0);
+                        mExpandedViewContainer.setTranslationX(0f);
                         mExpandedViewContainer.setTranslationY(getExpandedViewY());
                         mExpandedViewContainer.setAlpha(1f);
                     }
-                    if (mRelativeStackPositionBeforeRotation != null) {
-                        mStackAnimationController.setStackPosition(
-                                mRelativeStackPositionBeforeRotation);
-                        mRelativeStackPositionBeforeRotation = null;
-                    }
                     removeOnLayoutChangeListener(mOrientationChangedListener);
                 };
 
@@ -1178,26 +1152,16 @@
     }
 
     /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
-    public void onOrientationChanged(int orientation) {
-        mOrientation = orientation;
-
-        // Display size is based on the rotation device was in when requested, we should update it
-        // We use the real size & subtract screen decorations / window insets ourselves when needed
-        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
-
-        // Some resources change depending on orientation
+    public void onOrientationChanged() {
         Resources res = getContext().getResources();
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
 
         mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition();
-        addOnLayoutChangeListener(mOrientationChangedListener);
-        hideFlyoutImmediate();
-
         mManageMenu.setVisibility(View.INVISIBLE);
         mShowingManage = false;
+
+        addOnLayoutChangeListener(mOrientationChangedListener);
+        hideFlyoutImmediate();
     }
 
     /** Tells the views with locale-dependent layout direction to resolve the new direction. */
@@ -1217,11 +1181,7 @@
     public void onDisplaySizeChanged() {
         updateOverflow();
 
-        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
         Resources res = getContext().getResources();
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
         for (Bubble b : mBubbleData.getBubbles()) {
@@ -1231,8 +1191,8 @@
             }
             b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
         }
-        mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
-        mStackAnimationController.updateResources(mOrientation);
+        mExpandedAnimationController.updateResources();
+        mStackAnimationController.updateResources();
         mDismissView.updateResources();
         mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
     }
@@ -1682,7 +1642,8 @@
 
     private void animateExpansion() {
         cancelDelayedExpandCollapseSwitchAnimations();
-
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
         mIsExpanded = true;
         if (mStackEduView != null) {
             mStackEduView.hide(true /* fromExpansion */);
@@ -1698,20 +1659,21 @@
             }
         } /* after */);
 
-        mExpandedViewContainer.setTranslationX(0);
+        mExpandedViewContainer.setTranslationX(0f);
         mExpandedViewContainer.setTranslationY(getExpandedViewY());
         mExpandedViewContainer.setAlpha(1f);
 
         // X-value of the bubble we're expanding, once it's settled in its row.
-        final float bubbleWillBeAtX =
-                mExpandedAnimationController.getBubbleLeft(
+        final float bubbleWillBeAt =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(
                         mBubbleData.getBubbles().indexOf(mExpandedBubble));
 
         // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
         // that are animating farther, so that the expanded view doesn't move as much.
-        final float horizontalDistanceAnimated =
-                Math.abs(bubbleWillBeAtX
-                        - mStackAnimationController.getStackPosition().x);
+        final float relevantStackPosition = isLandscape
+                ? mStackAnimationController.getStackPosition().y
+                : mStackAnimationController.getStackPosition().x;
+        final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
 
         // Wait for the path animation target to reach its end, and add a small amount of extra time
         // if the bubble is moving a lot horizontally.
@@ -1721,13 +1683,26 @@
         if (getWidth() > 0) {
             startDelay = (long)
                     (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
-                            + (horizontalDistanceAnimated / getWidth()) * 30);
+                            + (distanceAnimated / getWidth()) * 30);
         }
 
         // Set the pivot point for the scale, so the expanded view animates out from the bubble.
-        mExpandedViewContainerMatrix.setScale(
-                0f, 0f,
-                bubbleWillBeAtX + mBubbleSize / 2f, getExpandedViewY());
+        if (isLandscape) {
+            float pivotX;
+            float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY());
+        }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
@@ -1747,9 +1722,11 @@
                         if (mExpandedBubble == null || mExpandedBubble.getIconView() == null) {
                             return;
                         }
+                        float translation = isLandscape
+                                ? mExpandedBubble.getIconView().getTranslationY()
+                                : mExpandedBubble.getIconView().getTranslationX();
                         mExpandedViewContainerMatrix.postTranslate(
-                                mExpandedBubble.getIconView().getTranslationX()
-                                        - bubbleWillBeAtX,
+                                translation - bubbleWillBeAt,
                                 0);
                         mExpandedViewContainer.setAnimationMatrix(
                                 mExpandedViewContainerMatrix);
@@ -1797,16 +1774,29 @@
         // We want to visually collapse into this bubble during the animation.
         final View expandingFromBubble = mExpandedBubble.getIconView();
 
-        // X-value the bubble is animating from (back into the stack).
-        final float expandingFromBubbleAtX =
-                mExpandedAnimationController.getBubbleLeft(
+        // Value the bubble is animating from (back into the stack).
+        final float expandingFromBubbleAt =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(
                         mBubbleData.getBubbles().indexOf(mExpandedBubble));
-
-        // Set the pivot point.
-        mExpandedViewContainerMatrix.setScale(
-                1f, 1f,
-                expandingFromBubbleAtX + mBubbleSize / 2f,
-                getExpandedViewY());
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
+        if (isLandscape) {
+            float pivotX;
+            float pivotY = expandingFromBubbleAt + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    1f, 1f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    1f, 1f,
+                    expandingFromBubbleAt + mBubbleSize / 2f,
+                    getExpandedViewY());
+        }
 
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
@@ -1815,9 +1805,15 @@
                 .addUpdateListener((target, values) -> {
                     if (expandingFromBubble != null) {
                         // Follow the bubble as it translates!
-                        mExpandedViewContainerMatrix.postTranslate(
-                                expandingFromBubble.getTranslationX()
-                                        - expandingFromBubbleAtX, 0f);
+                        if (isLandscape) {
+                            mExpandedViewContainerMatrix.postTranslate(
+                                    0f, expandingFromBubble.getTranslationY()
+                                            - expandingFromBubbleAt);
+                        } else {
+                            mExpandedViewContainerMatrix.postTranslate(
+                                    expandingFromBubble.getTranslationX()
+                                            - expandingFromBubbleAt, 0f);
+                        }
                     }
 
                     mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -1861,26 +1857,55 @@
         // The surface contains a screenshot of the animating out bubble, so we just need to animate
         // it out (and then release the GraphicBuffer).
         PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
-        PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+        PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
                 .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
                 .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
-                .spring(DynamicAnimation.TRANSLATION_Y,
-                        mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
-                        mTranslateSpringConfig)
-                .withEndActions(this::releaseAnimatingOutBubbleBuffer)
-                .start();
+                .withEndActions(this::releaseAnimatingOutBubbleBuffer);
+
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float translationX = mStackAnimationController.isStackOnLeftSide()
+                    ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
+                    : mAnimatingOutSurfaceContainer.getTranslationX();
+            animator.spring(DynamicAnimation.TRANSLATION_X,
+                    translationX,
+                    mTranslateSpringConfig)
+                    .start();
+        } else {
+            animator.spring(DynamicAnimation.TRANSLATION_Y,
+                    mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+                    mTranslateSpringConfig)
+                    .start();
+        }
 
         boolean isOverflow = mExpandedBubble != null
                 && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
-        float expandingFromBubbleDestinationX =
-                mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount()
+        float expandingFromBubbleDestination =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow
+                        ? getBubbleCount()
                         : mBubbleData.getBubbles().indexOf(mExpandedBubble));
 
         mExpandedViewContainer.setAlpha(1f);
         mExpandedViewContainer.setVisibility(View.VISIBLE);
 
-        mExpandedViewContainerMatrix.setScale(
-                0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY());
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float pivotX;
+            float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    expandingFromBubbleDestination + mBubbleSize / 2f,
+                    getExpandedViewY());
+        }
+
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
         mDelayedAnimationHandler.postDelayed(() -> {
@@ -2168,7 +2193,12 @@
      * Calculates the y position of the expanded view when it is expanded.
      */
     float getExpandedViewY() {
-        return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop;
+        final int top = mPositioner.getAvailableRect().top;
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            return top + mExpandedViewPadding;
+        } else {
+            return top + mBubbleSize + mBubblePaddingTop;
+        }
     }
 
     private boolean shouldShowFlyout(Bubble bubble) {
@@ -2216,9 +2246,10 @@
             }
 
             // Stop suppressing the dot now that the flyout has morphed into the dot.
-            bubble.getIconView().removeDotSuppressionFlag(
-                    BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
-
+            if (bubble.getIconView() != null) {
+                bubble.getIconView().removeDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
+            }
             // Hide the stack after a delay, if needed.
             updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
         };
@@ -2323,19 +2354,6 @@
         }
     }
 
-    private int getStatusBarHeight() {
-        if (getRootWindowInsets() != null) {
-            WindowInsets insets = getRootWindowInsets();
-            return Math.max(
-                    mStatusBarHeight,
-                    insets.getDisplayCutout() != null
-                            ? insets.getDisplayCutout().getSafeInsetTop()
-                            : 0);
-        }
-
-        return 0;
-    }
-
     private void requestUpdate() {
         if (mViewUpdatedRequested || mIsExpansionAnimating) {
             return;
@@ -2484,7 +2502,7 @@
         PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
         mAnimatingOutSurfaceContainer.setScaleX(1f);
         mAnimatingOutSurfaceContainer.setScaleY(1f);
-        mAnimatingOutSurfaceContainer.setTranslationX(0);
+        mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft());
         mAnimatingOutSurfaceContainer.setTranslationY(0);
 
         final int[] activityViewLocation =
@@ -2542,9 +2560,22 @@
             Log.d(TAG, "updateExpandedView: mIsExpanded=" + mIsExpanded);
         }
 
+        // Need to update the padding around the view for any insets
+        Insets insets = mPositioner.getInsets();
+        int leftPadding = insets.left + mExpandedViewPadding;
+        int rightPadding = insets.right + mExpandedViewPadding;
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            if (!mStackAnimationController.isStackOnLeftSide()) {
+                rightPadding += mPointerHeight + mBubbleSize;
+            } else {
+                leftPadding += mPointerHeight + mBubbleSize;
+            }
+        }
+        mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0);
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
             mExpandedViewContainer.setTranslationY(getExpandedViewY());
+            mExpandedViewContainer.setTranslationX(0f);
             mExpandedBubble.getExpandedView().updateView(
                     mExpandedViewContainer.getLocationOnScreen());
         }
@@ -2587,12 +2618,27 @@
         if (index == -1) {
             return;
         }
-        float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
-        float halfBubble = mBubbleSize / 2f;
-        float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
-        // Padding might be adjusted for insets, so get it directly from the view
-        bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-        mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
+        float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index);
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float x = mStackOnLeftOrWillBe
+                    ? mPositioner.getAvailableRect().left
+                    : mPositioner.getAvailableRect().right
+                            - mExpandedViewContainer.getPaddingRight()
+                            - mPointerHeight;
+            float bubbleCenter = bubblePosition - getExpandedViewY() + (mBubbleSize / 2f);
+            mExpandedBubble.getExpandedView().setPointerPosition(
+                    x,
+                    bubbleCenter,
+                    true,
+                    mStackOnLeftOrWillBe);
+        } else {
+            float bubbleCenter = bubblePosition + (mBubbleSize / 2f);
+            mExpandedBubble.getExpandedView().setPointerPosition(
+                    bubbleCenter,
+                    getExpandedViewY(),
+                    false,
+                    mStackOnLeftOrWillBe);
+        }
     }
 
     /**
@@ -2621,7 +2667,7 @@
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedXPosition() {
-        return new BigDecimal(getStackPosition().x / mDisplaySize.x)
+        return new BigDecimal(getStackPosition().x / mPositioner.getAvailableRect().width())
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -2630,7 +2676,7 @@
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedYPosition() {
-        return new BigDecimal(getStackPosition().y / mDisplaySize.y)
+        return new BigDecimal(getStackPosition().y / mPositioner.getAvailableRect().height())
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
index 39c750d..4882abc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.wm.shell.ShellTaskOrganizer;
 
 import java.util.List;
 
@@ -129,5 +130,8 @@
     void setOverflowListener(BubbleData.Listener listener);
 
     /** The task listener for events in bubble tasks. **/
-    MultiWindowTaskListener getTaskManager();
+    ShellTaskOrganizer getTaskOrganizer();
+
+    /** Contains information to help position things on the screen.  */
+    BubblePositioner getPositioner();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
deleted file mode 100644
index 351a268..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 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.bubbles;
-
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.window.TaskOrganizer;
-import android.window.WindowContainerToken;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-
-import java.util.ArrayList;
-
-/**
- * Manages tasks that are displayed in multi-window (e.g. bubbles). These are displayed in a
- * {@link TaskView}.
- *
- * This class listens on {@link TaskOrganizer} callbacks for events. Once visible, these tasks will
- * intercept back press events.
- *
- * @see android.app.WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW
- * @see TaskView
- */
-// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
-public class MultiWindowTaskListener implements ShellTaskOrganizer.TaskListener {
-    private static final String TAG = MultiWindowTaskListener.class.getSimpleName();
-
-    private static final boolean DEBUG = false;
-
-    //TODO(b/170153209): Have shell listener allow per task registration and remove this.
-    public interface Listener {
-        void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash);
-        void onTaskVanished(RunningTaskInfo taskInfo);
-        void onTaskInfoChanged(RunningTaskInfo taskInfo);
-        void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo);
-    }
-
-    private static class TaskData {
-        final RunningTaskInfo taskInfo;
-        final Listener listener;
-
-        TaskData(RunningTaskInfo info, Listener l) {
-            taskInfo = info;
-            listener = l;
-        }
-    }
-
-    private final Handler mHandler;
-    private final ShellTaskOrganizer mTaskOrganizer;
-    private final ArrayMap<WindowContainerToken, TaskData> mTasks = new ArrayMap<>();
-
-    private ArrayMap<IBinder, Listener> mLaunchCookieToListener = new ArrayMap<>();
-
-    /**
-     * Create a listener for tasks in multi-window mode.
-     */
-    public MultiWindowTaskListener(Handler handler, ShellTaskOrganizer organizer) {
-        mHandler = handler;
-        mTaskOrganizer = organizer;
-        mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_MULTI_WINDOW);
-    }
-
-    /**
-     * @return the task organizer that is listened to.
-     */
-    public TaskOrganizer getTaskOrganizer() {
-        return mTaskOrganizer;
-    }
-
-    public void setPendingLaunchCookieListener(IBinder cookie, Listener listener) {
-        mLaunchCookieToListener.put(cookie, listener);
-    }
-
-    /**
-     * Removes a task listener previously registered when starting a new activity.
-     */
-    public void removeListener(Listener listener) {
-        if (DEBUG) {
-            Log.d(TAG, "removeListener: listener=" + listener);
-        }
-        for (int i = 0; i < mTasks.size(); i++) {
-            if (mTasks.valueAt(i).listener == listener) {
-                mTasks.removeAt(i);
-            }
-        }
-    }
-
-    @Override
-    public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
-        if (DEBUG) {
-            Log.d(TAG, "onTaskAppeared: taskInfo=" + taskInfo);
-        }
-
-        // We only care about task we launch which should all have a tracking launch cookie.
-        final ArrayList<IBinder> launchCookies = taskInfo.launchCookies;
-        if (launchCookies.isEmpty()) return;
-
-        // See if this task has one of our launch cookies.
-        Listener listener = null;
-        for (int i = launchCookies.size() - 1; i >= 0; --i) {
-            final IBinder cookie = launchCookies.get(i);
-            listener = mLaunchCookieToListener.get(cookie);
-            if (listener != null) {
-                mLaunchCookieToListener.remove(cookie);
-                break;
-            }
-        }
-
-        // This is either not a task we launched or we have handled it previously.
-        if (listener == null) return;
-
-        mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, true);
-
-        final TaskData data = new TaskData(taskInfo, listener);
-        mTasks.put(taskInfo.token, data);
-        mHandler.post(() -> data.listener.onTaskAppeared(taskInfo, leash));
-    }
-
-    @Override
-    public void onTaskVanished(RunningTaskInfo taskInfo) {
-        final TaskData data = mTasks.remove(taskInfo.token);
-        if (data == null) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "onTaskVanished: taskInfo=" + taskInfo + " listener=" + data.listener);
-        }
-        mHandler.post(() -> data.listener.onTaskVanished(taskInfo));
-    }
-
-    @Override
-    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
-        final TaskData data = mTasks.get(taskInfo.token);
-        if (data == null) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
-        }
-        mHandler.post(() -> data.listener.onTaskInfoChanged(taskInfo));
-    }
-
-    @Override
-    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-        final TaskData data = mTasks.get(taskInfo.token);
-        if (data == null) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
-        }
-        mHandler.post(() -> data.listener.onBackPressedOnTaskRoot(taskInfo));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
index b78e677..85616d1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
@@ -30,20 +30,26 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Handler;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.wm.shell.ShellTaskOrganizer;
+
 import dalvik.system.CloseGuard;
 
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
 /**
  * View that can display a task.
  */
 // TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
 public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
-        MultiWindowTaskListener.Listener {
+        ShellTaskOrganizer.TaskListener {
 
     public interface Listener {
         /** Called when the container is ready for launching activities. */
@@ -67,7 +73,7 @@
 
     private final CloseGuard mGuard = CloseGuard.get();
 
-    private final MultiWindowTaskListener mMultiWindowTaskListener;
+    private final ShellTaskOrganizer mTaskOrganizer;
 
     private ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mTaskToken;
@@ -76,14 +82,16 @@
     private boolean mSurfaceCreated;
     private boolean mIsInitialized;
     private Listener mListener;
+    private final Executor mExecutor;
 
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpRootRect = new Rect();
 
-    public TaskView(Context context, MultiWindowTaskListener taskListener) {
+    public TaskView(Context context, ShellTaskOrganizer organizer, Executor executor) {
         super(context, null, 0, 0, true /* disableBackgroundLayer */);
 
-        mMultiWindowTaskListener = taskListener;
+        mExecutor = executor;
+        mTaskOrganizer = organizer;
         setUseAlpha();
         getHolder().addCallback(this);
         mGuard.open("release");
@@ -142,7 +150,7 @@
 
     private void prepareActivityOptions(ActivityOptions options) {
         final Binder launchCookie = new Binder();
-        mMultiWindowTaskListener.setPendingLaunchCookieListener(launchCookie, this);
+        mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
         options.setLaunchCookie(launchCookie);
         options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         options.setTaskAlwaysOnTop(true);
@@ -165,7 +173,7 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setBounds(mTaskToken, mTmpRect);
         // TODO(b/151449487): Enable synchronization
-        mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
     }
 
     /**
@@ -189,7 +197,7 @@
 
     private void performRelease() {
         getHolder().removeCallback(this);
-        mMultiWindowTaskListener.removeListener(this);
+        mTaskOrganizer.removeListener(this);
         resetTaskInfo();
         mGuard.close();
         if (mListener != null && mIsInitialized) {
@@ -207,7 +215,7 @@
     private void updateTaskVisibility() {
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
-        mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
         // TODO(b/151449487): Only call callback once we enable synchronization
         if (mListener != null) {
             mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
@@ -217,33 +225,37 @@
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl leash) {
-        mTaskInfo = taskInfo;
-        mTaskToken = taskInfo.token;
-        mTaskLeash = leash;
+        mExecutor.execute(() -> {
+            mTaskInfo = taskInfo;
+            mTaskToken = taskInfo.token;
+            mTaskLeash = leash;
 
-        if (mSurfaceCreated) {
-            // Surface is ready, so just reparent the task to this surface control
-            mTransaction.reparent(mTaskLeash, getSurfaceControl())
-                    .show(mTaskLeash)
-                    .apply();
-        } else {
-            // The surface has already been destroyed before the task has appeared, so go ahead and
-            // hide the task entirely
-            updateTaskVisibility();
-        }
+            if (mSurfaceCreated) {
+                // Surface is ready, so just reparent the task to this surface control
+                mTransaction.reparent(mTaskLeash, getSurfaceControl())
+                        .show(mTaskLeash)
+                        .apply();
+            } else {
+                // The surface has already been destroyed before the task has appeared,
+                // so go ahead and hide the task entirely
+                updateTaskVisibility();
+            }
 
-        // TODO: Synchronize show with the resize
-        onLocationChanged();
-        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+            // TODO: Synchronize show with the resize
+            onLocationChanged();
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
 
-        if (mListener != null) {
-            mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
-        }
+            if (mListener != null) {
+                mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+            }
+        });
     }
 
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-        if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+        mExecutor.execute(() -> {
+            if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+
             if (mListener != null) {
                 mListener.onTaskRemovalStarted(taskInfo.taskId);
             }
@@ -251,22 +263,37 @@
             // Unparent the task when this surface is destroyed
             mTransaction.reparent(mTaskLeash, null).apply();
             resetTaskInfo();
-        }
+        });
     }
 
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-        mTaskInfo.taskDescription = taskInfo.taskDescription;
-        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        mExecutor.execute(() -> {
+            mTaskInfo.taskDescription = taskInfo.taskDescription;
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        });
     }
 
     @Override
     public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
-        if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+        mExecutor.execute(() -> {
+            if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
             if (mListener != null) {
                 mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
             }
-        }
+        });
+    }
+
+    @Override
+    public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+    }
+
+    @Override
+    public String toString() {
+        return "TaskView" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 7fdc019..5a70401 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -19,11 +19,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.PointF;
-import android.view.DisplayCutout;
+import android.graphics.Rect;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -32,6 +30,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 
@@ -83,16 +82,8 @@
     private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
-    /** Space between bubbles in row above expanded view. */
-    private float mSpaceBetweenBubbles;
-    /** Height of the status bar. */
-    private float mStatusBarHeight;
-    /** Size of display. */
-    private Point mDisplaySize;
     /** Max number of bubbles shown in row above expanded view. */
     private int mBubblesMaxRendered;
-    /** What the current screen orientation is. */
-    private int mScreenOrientation;
 
     private boolean mAnimatingExpand = false;
 
@@ -104,7 +95,8 @@
     private boolean mPreparingToCollapse = false;
 
     private boolean mAnimatingCollapse = false;
-    private @Nullable Runnable mAfterExpand;
+    @Nullable
+    private Runnable mAfterExpand;
     private Runnable mAfterCollapse;
     private PointF mCollapsePoint;
 
@@ -138,9 +130,12 @@
      */
     private Runnable mOnBubbleAnimatedOutAction;
 
-    public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
-            int orientation, Runnable onBubbleAnimatedOutAction) {
-        updateResources(orientation, displaySize);
+    private BubblePositioner mPositioner;
+
+    public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding,
+            Runnable onBubbleAnimatedOutAction) {
+        mPositioner = positioner;
+        updateResources();
         mExpandedViewPadding = expandedViewPadding;
         mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
     }
@@ -152,7 +147,8 @@
     private boolean mBubbleDraggedOutEnough = false;
 
     /** End action to run when the lead bubble's expansion animation completes. */
-    @Nullable private Runnable mLeadBubbleEndAction;
+    @Nullable
+    private Runnable mLeadBubbleEndAction;
 
     /**
      * Animates expanding the bubbles into a row along the top of the screen, optionally running an
@@ -200,28 +196,17 @@
 
     /**
      * Update effective screen width based on current orientation.
-     * @param orientation Landscape or portrait.
-     * @param displaySize Updated display size.
      */
-    public void updateResources(int orientation, Point displaySize) {
-        mScreenOrientation = orientation;
-        mDisplaySize = displaySize;
+    public void updateResources() {
         if (mLayout == null) {
             return;
         }
         Resources res = mLayout.getContext().getResources();
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
-
-        // Includes overflow button.
-        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
-                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
-        mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
     }
 
     /**
@@ -270,9 +255,18 @@
                 // If we're expanding, first draw a line from the bubble's current position to the
                 // top of the screen.
                 path.lineTo(bubble.getTranslationX(), expandedY);
-
                 // Then, draw a line across the screen to the bubble's resting position.
-                path.lineTo(getBubbleLeft(index), expandedY);
+                if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+                    Rect availableRect = mPositioner.getAvailableRect();
+                    boolean onLeft = mCollapsePoint != null
+                            && mCollapsePoint.x < (availableRect.width() / 2f);
+                    float translationX = onLeft
+                            ? availableRect.left + mExpandedViewPadding
+                            : availableRect.right - mBubbleSizePx - mExpandedViewPadding;
+                    path.lineTo(translationX, getBubbleXOrYForOrientation(index));
+                } else {
+                    path.lineTo(getBubbleXOrYForOrientation(index), expandedY);
+                }
             } else {
                 final float stackedX = mCollapsePoint.x;
 
@@ -411,7 +405,8 @@
         updateBubblePositions();
     }
 
-    @Nullable public View getDraggedOutBubble() {
+    @Nullable
+    public View getDraggedOutBubble() {
         return mMagnetizedBubbleDraggingOut == null
                 ? null
                 : mMagnetizedBubbleDraggingOut.getUnderlyingObject();
@@ -430,7 +425,7 @@
         final int index = mLayout.indexOfChild(bubbleView);
 
         animationForChildAtIndex(index)
-                .position(getBubbleLeft(index), getExpandedY())
+                .position(getBubbleXOrYForOrientation(index), getExpandedY())
                 .withPositionStartVelocities(velX, velY)
                 .start(() -> bubbleView.setTranslationZ(0f) /* after */);
 
@@ -457,15 +452,7 @@
 
     /** The Y value of the row of expanded bubbles. */
     public float getExpandedY() {
-        if (mLayout == null || mLayout.getRootWindowInsets() == null) {
-            return 0;
-        }
-        final WindowInsets insets = mLayout.getRootWindowInsets();
-        return mBubblePaddingTop + Math.max(
-                mStatusBarHeight,
-                insets.getDisplayCutout() != null
-                        ? insets.getDisplayCutout().getSafeInsetTop()
-                        : 0);
+        return mPositioner.getAvailableRect().top + mBubblePaddingTop;
     }
 
     /** Description of current animation controller state. */
@@ -479,7 +466,7 @@
 
     @Override
     void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
-        updateResources(mScreenOrientation, mDisplaySize);
+        updateResources();
 
         // Ensure that all child views are at 1x scale, and visible, in case they were animating
         // in.
@@ -524,7 +511,7 @@
         } else if (mAnimatingCollapse) {
             startOrUpdatePathAnimation(false /* expanding */);
         } else {
-            child.setTranslationX(getBubbleLeft(index));
+            child.setTranslationX(getBubbleXOrYForOrientation(index));
 
             // If we're preparing to collapse, don't start animations since the collapse animation
             // will take over and animate the new bubble into the correct (stacked) position.
@@ -593,76 +580,56 @@
                 return;
             }
 
-            animationForChild(bubble)
-                    .translationX(getBubbleLeft(i))
-                    .start();
-        }
-    }
-
-    /**
-     * @param index Bubble index in row.
-     * @return Bubble left x from left edge of screen.
-     */
-    public float getBubbleLeft(int index) {
-        final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles);
-        return getRowLeft() + bubbleFromRowLeft;
-    }
-
-    /**
-     * When expanded, the bubbles are centered in the screen. In portrait, all available space is
-     * used. In landscape we have too much space so the value is restricted. This method accounts
-     * for window decorations (nav bar, cutouts).
-     *
-     * @return the desired width to display the expanded bubbles in.
-     */
-    public float getWidthForDisplayingBubbles() {
-        final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
-        if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            // display size y in landscape will be the smaller dimension of the screen
-            return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT);
-        } else {
-            return availableWidth;
-        }
-    }
-
-    /**
-     * Determines the available screen width without the cutout.
-     *
-     * @param subtractStableInsets Whether or not stable insets should also be removed from the
-     *                             returned width.
-     * @return the total screen width available accounting for cutouts and insets,
-     * iff {@param includeStableInsets} is true.
-     */
-    private float getAvailableScreenWidth(boolean subtractStableInsets) {
-        float availableSize = mDisplaySize.x;
-        WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
-        if (insets != null) {
-            int cutoutLeft = 0;
-            int cutoutRight = 0;
-            DisplayCutout cutout = insets.getDisplayCutout();
-            if (cutout != null) {
-                cutoutLeft = cutout.getSafeInsetLeft();
-                cutoutRight = cutout.getSafeInsetRight();
+            if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+                Rect availableRect = mPositioner.getAvailableRect();
+                boolean onLeft = mCollapsePoint != null
+                        && mCollapsePoint.x < (availableRect.width() / 2f);
+                animationForChild(bubble)
+                        .translationX(onLeft
+                                ? availableRect.left + mExpandedViewPadding
+                                : availableRect.right - mBubbleSizePx - mExpandedViewPadding)
+                        .translationY(getBubbleXOrYForOrientation(i))
+                        .start();
+            } else {
+                animationForChild(bubble)
+                        .translationX(getBubbleXOrYForOrientation(i))
+                        .translationY(getExpandedY())
+                        .start();
             }
-            final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0;
-            final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0;
-            availableSize -= Math.max(stableLeft, cutoutLeft);
-            availableSize -= Math.max(stableRight, cutoutRight);
         }
-        return availableSize;
     }
 
-    private float getRowLeft() {
+    /**
+     * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
+     * row. When in landscape, they show at the left or right side in a vertical row. This method
+     * accounts for screen orientation and will return an x or y value for the position of the
+     * bubble in the row.
+     *
+     * @param index Bubble index in row.
+     * @return the y position of the bubble if {@link Configuration#ORIENTATION_LANDSCAPE} and the
+     * x position if {@link Configuration#ORIENTATION_PORTRAIT}.
+     */
+    public float getBubbleXOrYForOrientation(int index) {
         if (mLayout == null) {
             return 0;
         }
-        float rowWidth = (mLayout.getChildCount() * mBubbleSizePx)
-                + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
-
-        // This display size we're using includes the size of the insets, we want the true
-        // center of the display minus the notch here, which means we should include the
-        // stable insets (e.g. status bar, nav bar) in this calculation.
-        final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
-        return trueCenter - (rowWidth / 2f);
+        Rect availableRect = mPositioner.getAvailableRect();
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
+        final float availableSpace = isLandscape
+                ? availableRect.height()
+                : availableRect.width();
+        final float spaceForMaxBubbles = (mExpandedViewPadding * 2)
+                + (mBubblesMaxRendered + 1) * mBubbleSizePx;
+        final float spaceBetweenBubbles =
+                (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered;
+        final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
+                + ((mLayout.getChildCount() - 1) * spaceBetweenBubbles);
+        final float centerPosition = isLandscape
+                ? availableRect.centerY()
+                : availableRect.centerX();
+        final float rowStart = centerPosition - (expandedStackSize / 2f);
+        final float positionInBar = index * (mBubbleSizePx + spaceBetweenBubbles);
+        return rowStart + positionInBar;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 1205124..31e1ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -24,7 +24,6 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -35,6 +34,7 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.systemui.bubbles.BubbleStackView;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -59,12 +59,6 @@
 
     private static final String TAG = "Bubbs.StackCtrl";
 
-    /** Scale factor to use initially for new bubbles being animated in. */
-    private static final float ANIMATE_IN_STARTING_SCALE = 1.15f;
-
-    /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */
-    private static final int ANIMATE_TRANSLATION_FACTOR = 4;
-
     /** Values to use for animating bubbles in. */
     private static final float ANIMATE_IN_STIFFNESS = 1000f;
     private static final int ANIMATE_IN_START_DELAY = 25;
@@ -198,10 +192,8 @@
     private int mBubblePaddingTop;
     /** How far offscreen the stack rests. */
     private int mBubbleOffscreen;
-    /** How far down the screen the stack starts, when there is no pre-existing location. */
-    private int mStackStartingVerticalOffset;
-    /** Height of the status bar. */
-    private float mStatusBarHeight;
+    /** Contains display size, orientation, and inset information. */
+    private BubblePositioner mPositioner;
 
     /** FloatingContentCoordinator instance for resolving floating content conflicts. */
     private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -266,10 +258,12 @@
     public StackAnimationController(
             FloatingContentCoordinator floatingContentCoordinator,
             IntSupplier bubbleCountSupplier,
-            Runnable onBubbleAnimatedOutAction) {
+            Runnable onBubbleAnimatedOutAction,
+            BubblePositioner positioner) {
         mFloatingContentCoordinator = floatingContentCoordinator;
         mBubbleCountSupplier = bubbleCountSupplier;
         mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
+        mPositioner = positioner;
     }
 
     /**
@@ -583,45 +577,12 @@
      * be animated or dragged beyond them.
      */
     public RectF getAllowableStackPositionRegion() {
-        final WindowInsets insets = mLayout.getRootWindowInsets();
-        final RectF allowableRegion = new RectF();
-        if (insets != null) {
-            allowableRegion.left =
-                    -mBubbleOffscreen
-                            + Math.max(
-                            insets.getSystemWindowInsetLeft(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetLeft()
-                                    : 0);
-            allowableRegion.right =
-                    mLayout.getWidth()
-                            - mBubbleSize
-                            + mBubbleOffscreen
-                            - Math.max(
-                            insets.getSystemWindowInsetRight(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetRight()
-                                    : 0);
-
-            allowableRegion.top =
-                    mBubblePaddingTop
-                            + Math.max(
-                            mStatusBarHeight,
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetTop()
-                                    : 0);
-            allowableRegion.bottom =
-                    mLayout.getHeight()
-                            - mBubbleSize
-                            - mBubblePaddingTop
-                            - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f)
-                            - Math.max(
-                            insets.getStableInsetBottom(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetBottom()
-                                    : 0);
-        }
-
+        final RectF allowableRegion = new RectF(mPositioner.getAvailableRect());
+        allowableRegion.left -= mBubbleOffscreen;
+        allowableRegion.top += mBubblePaddingTop;
+        allowableRegion.right += mBubbleOffscreen - mBubbleSize;
+        allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize
+                + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f);
         return allowableRegion;
     }
 
@@ -824,22 +785,15 @@
         mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
-        mStackStartingVerticalOffset =
-                res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
-        mStatusBarHeight =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
-     * Update effective screen width based on current orientation.
-     * @param orientation Landscape or portrait.
+     * Update resources.
      */
-    public void updateResources(int orientation) {
+    public void updateResources() {
         if (mLayout != null) {
             Resources res = mLayout.getContext().getResources();
             mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-            mStatusBarHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.status_bar_height);
         }
     }
 
@@ -976,19 +930,19 @@
             return;
         }
 
-        final float xOffset =
-                getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+        final float yOffset =
+                getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y);
 
         // Position the new bubble in the correct position, scaled down completely.
-        child.setTranslationX(mStackPosition.x + xOffset * index);
-        child.setTranslationY(mStackPosition.y);
+        child.setTranslationX(mStackPosition.x);
+        child.setTranslationY(mStackPosition.y + yOffset * index);
         child.setScaleX(0f);
         child.setScaleY(0f);
 
         // Push the subsequent views out of the way, if there are subsequent views.
         if (index + 1 < mLayout.getChildCount()) {
             animationForChildAtIndex(index + 1)
-                    .translationX(mStackPosition.x + xOffset * (index + 1))
+                    .translationY(mStackPosition.y + yOffset * (index + 1))
                     .withStiffness(SpringForce.STIFFNESS_LOW)
                     .start();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index cb90b61..3aa4626 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
@@ -340,13 +339,6 @@
         return Choreographer.getInstance();
     }
 
-    /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
-    @Provides
-    @SysUISingleton
-    static UiEventLogger provideUiEventLogger() {
-        return new UiEventLoggerImpl();
-    }
-
     /** */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index c5dc8cc..554d9cb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -16,9 +16,20 @@
 
 package com.android.systemui.dagger;
 
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
+import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+
+import javax.inject.Singleton;
 
 import dagger.Module;
+import dagger.Provides;
 
 /**
  * Supplies globally scoped instances that should be available in all versions of SystemUI
@@ -39,4 +50,38 @@
         FrameworkServicesModule.class,
         GlobalConcurrencyModule.class})
 public class GlobalModule {
+
+    // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
+    //                    Bubbles has migrated over
+    @Singleton
+    @Provides
+    static FloatingContentCoordinator provideFloatingContentCoordinator() {
+        return new FloatingContentCoordinator();
+    }
+
+    // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
+    //                    Bubbles has migrated over
+    @Singleton
+    @Provides
+    static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
+        return new WindowManagerShellWrapper();
+    }
+
+    // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and
+    //                    callers should be creating a new builder on demand
+    @Singleton
+    @Provides
+    static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
+            Context context) {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        context.getDisplay().getMetrics(displayMetrics);
+        return new FlingAnimationUtils.Builder(displayMetrics);
+    }
+
+    /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
+    @Provides
+    @Singleton
+    static UiEventLogger provideUiEventLogger() {
+        return new UiEventLoggerImpl();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 00fdf55..d648c94 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -52,7 +52,7 @@
     WMComponent.Builder getWMComponentBuilder();
 
     /**
-     * Builder for a SysuiComponent.
+     * Builder for a SysUIComponent.
      */
     SysUIComponent.Builder getSysUIComponent();
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 4bea067..b098579 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -22,8 +22,15 @@
 import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
@@ -43,15 +50,35 @@
     /**
      * Builder for a SysUIComponent.
      */
+    @SysUISingleton
     @Subcomponent.Builder
     interface Builder {
         @BindsInstance
-        Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass);
+        Builder setPip(Optional<Pip> p);
+
+        @BindsInstance
+        Builder setSplitScreen(Optional<SplitScreen> s);
+
+        @BindsInstance
+        Builder setOneHanded(Optional<OneHanded> o);
+
+        @BindsInstance
+        Builder setInputConsumerController(InputConsumerController i);
+
+        @BindsInstance
+        Builder setShellTaskOrganizer(ShellTaskOrganizer s);
 
         SysUIComponent build();
     }
 
     /**
+     * Initializes all the SysUI components.
+     */
+    default void init() {
+        // Do nothing
+    }
+
+    /**
      * Provides a BootCompleteCache.
      */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 2c0b04f..7ca8e63 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -33,6 +33,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
@@ -61,7 +62,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.wmshell.WMShellModule;
 
 import javax.inject.Named;
 
@@ -74,9 +74,9 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            QSModule.class,
-            WMShellModule.class
-        })
+        MediaModule.class,
+        QSModule.class
+})
 public abstract class SystemUIDefaultModule {
 
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 63d9a83..a982ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -21,6 +21,7 @@
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.doze.dagger.DozeComponent;
@@ -125,6 +126,9 @@
     @BindsOptionalOf
     abstract StatusBar optionalStatusBar();
 
+    @BindsOptionalOf
+    abstract Bubbles optionalBubbles();
+
     @SysUISingleton
     @Binds
     abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index ad90eff..e3bd1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,7 +16,15 @@
 
 package com.android.systemui.dagger;
 
-import javax.inject.Inject;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.wmshell.WMShellModule;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
 
 import dagger.Subcomponent;
 
@@ -24,7 +32,7 @@
  * Dagger Subcomponent for WindowManager.
  */
 @WMSingleton
-@Subcomponent(modules = {})
+@Subcomponent(modules = {WMShellModule.class})
 public interface WMComponent {
 
     /**
@@ -35,18 +43,36 @@
         WMComponent build();
     }
 
-
     /**
-     *  Example class used for passing an API to SysUI from WMShell.
-     *
-     *  TODO: Remove this once real WM classes are ready to go.
-     **/
-    @WMSingleton
-    class StubAPIClass {
-        @Inject
-        StubAPIClass() {}
+     * Initializes all the WMShell components before starting any of the SystemUI components.
+     */
+    default void init() {
+        // This is to prevent circular init problem by separating registration step out of its
+        // constructor. And make sure the initialization of DisplayImeController won't depend on
+        // specific feature anymore.
+        getDisplayImeController().startMonitorDisplays();
+        getShellTaskOrganizer().registerOrganizer();
     }
 
-    /** Create a StubAPIClass. */
-    StubAPIClass createStubAPIClass();
+    // Required components to be initialized at start up
+    @WMSingleton
+    ShellTaskOrganizer getShellTaskOrganizer();
+
+    @WMSingleton
+    DisplayImeController getDisplayImeController();
+
+    @WMSingleton
+    InputConsumerController getInputConsumerController();
+
+    // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
+    //                    depending on the threading mechanism we go with
+
+    @WMSingleton
+    Optional<OneHanded> getOneHanded();
+
+    @WMSingleton
+    Optional<Pip> getPip();
+
+    @WMSingleton
+    Optional<SplitScreen> getSplitScreen();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 5f726cd..37bcb16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -316,7 +316,8 @@
             }
             mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
             mPendingIntent = PendingIntent.getActivity(getContext(), 0,
-                    new Intent(getContext(), KeyguardSliceProvider.class), 0);
+                    new Intent(getContext(), KeyguardSliceProvider.class),
+                    PendingIntent.FLAG_IMMUTABLE);
             try {
                 //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
                 mMediaManager.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 094ece2..6fb8650 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -18,6 +18,7 @@
 
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.dagger.MediaModule.KEYGUARD
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
@@ -25,6 +26,7 @@
 import com.android.systemui.statusbar.notification.stack.MediaHeaderView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import javax.inject.Inject
+import javax.inject.Named
 
 /**
  * A class that controls the media notifications on the lock screen, handles its visibility and
@@ -32,7 +34,7 @@
  */
 @SysUISingleton
 class KeyguardMediaController @Inject constructor(
-    private val mediaHost: MediaHost,
+    @param:Named(KEYGUARD) private val mediaHost: MediaHost,
     private val bypassController: KeyguardBypassController,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val notifLockscreenUserManager: NotificationLockscreenUserManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index ce184aa..857c50f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -11,7 +11,7 @@
 import java.util.Objects
 import javax.inject.Inject
 
-class MediaHost @Inject constructor(
+class MediaHost constructor(
     private val state: MediaHostStateHolder,
     private val mediaHierarchyManager: MediaHierarchyManager,
     private val mediaDataManager: MediaDataManager,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 51dbfa7..ce72991 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -46,7 +46,7 @@
     /**
      * Callback representing that a media object is now expired:
      * @param token Media session unique identifier
-     * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT}
+     * @param pauseTimeout True when expired for {@code PAUSED_MEDIA_TIMEOUT}
      */
     lateinit var timeoutCallback: (String, Boolean) -> Unit
 
@@ -57,11 +57,10 @@
         // Having an old key means that we're migrating from/to resumption. We should update
         // the old listener to make sure that events will be dispatched to the new location.
         val migrating = oldKey != null && key != oldKey
-        var wasPlaying = false
         if (migrating) {
             val reusedListener = mediaListeners.remove(oldKey)
             if (reusedListener != null) {
-                wasPlaying = reusedListener.playing ?: false
+                val wasPlaying = reusedListener.playing ?: false
                 if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
                 reusedListener.mediaData = data
                 reusedListener.key = key
@@ -159,9 +158,8 @@
                         Log.v(TAG, "Execute timeout for $key")
                     }
                     timedOut = true
-                    if (dispatchEvents) {
-                        timeoutCallback(key, timedOut)
-                    }
+                    // this event is async, so it's safe even when `dispatchEvents` is false
+                    timeoutCallback(key, timedOut)
                 }, PAUSED_MEDIA_TIMEOUT)
             } else {
                 expireMediaTimeout(key, "playback started - $state, $key")
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 9e326aa..c824458 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -91,9 +91,9 @@
         }
     private var playbackState: PlaybackState? = null
     private var callback = object : MediaController.Callback() {
-        override fun onPlaybackStateChanged(state: PlaybackState) {
+        override fun onPlaybackStateChanged(state: PlaybackState?) {
             playbackState = state
-            if (PlaybackState.STATE_NONE.equals(playbackState)) {
+            if (playbackState == null || PlaybackState.STATE_NONE.equals(playbackState)) {
                 clearController()
             } else {
                 checkIfPollingNeeded()
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
new file mode 100644
index 0000000..57ac9df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.media.dagger;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.media.MediaDataManager;
+import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.media.MediaHostStatesManager;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for the media package. */
+@Module
+public interface MediaModule {
+    String QS_PANEL = "media_qs_panel";
+    String QUICK_QS_PANEL = "media_quick_qs_panel";
+    String KEYGUARD = "media_keyguard";
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(QS_PANEL)
+    static MediaHost providesQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(QUICK_QS_PANEL)
+    static MediaHost providesQuickQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(KEYGUARD)
+    static MediaHost providesKeyguardMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index eeb93bb..bdaeb13 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.INotificationManager;
+import android.app.people.ConversationChannel;
 import android.app.people.IPeopleManager;
 import android.content.Context;
 import android.content.pm.LauncherApps;
@@ -29,6 +30,7 @@
 import android.os.Bundle;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
 import android.util.Log;
 import android.view.ViewGroup;
@@ -52,7 +54,6 @@
     private INotificationManager mNotificationManager;
     private PackageManager mPackageManager;
     private LauncherApps mLauncherApps;
-    private List<ConversationChannelWrapper> mConversations;
     private Context mContext;
 
     @Override
@@ -77,15 +78,25 @@
      */
     private void setTileViewsWithPriorityConversations() {
         try {
+            boolean showAllConversations = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0;
             List<ConversationChannelWrapper> conversations =
                     mNotificationManager.getConversations(
-                            true /* priority only */).getList();
-            mConversations = conversations.stream().filter(
-                    c -> shouldKeepConversation(c)).collect(Collectors.toList());
-            for (ConversationChannelWrapper conversation : mConversations) {
+                            !showAllConversations /* priority only */).getList();
+            List<ShortcutInfo> shortcutInfos = conversations.stream().filter(
+                    c -> shouldKeepConversation(c)).map(c -> c.getShortcutInfo()).collect(
+                    Collectors.toList());
+            if (showAllConversations) {
+                List<ConversationChannel> recentConversations =
+                        mPeopleManager.getRecentConversations().getList();
+                List<ShortcutInfo> recentShortcuts = recentConversations.stream().map(
+                        c -> c.getShortcutInfo()).collect(Collectors.toList());
+                shortcutInfos.addAll(recentShortcuts);
+            }
+            for (ShortcutInfo conversation : shortcutInfos) {
                 PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext,
                         mPeopleSpaceLayout,
-                        conversation.getShortcutInfo().getId());
+                        conversation.getId());
                 setTileView(tileView, conversation);
             }
         } catch (Exception e) {
@@ -95,11 +106,10 @@
 
     /** Sets {@code tileView} with the data in {@code conversation}. */
     private void setTileView(PeopleSpaceTileView tileView,
-            ConversationChannelWrapper conversation) {
+            ShortcutInfo shortcutInfo) {
         try {
-            ShortcutInfo shortcutInfo = conversation.getShortcutInfo();
             int userId = UserHandle.getUserHandleForUid(
-                    conversation.getUid()).getIdentifier();
+                    shortcutInfo.getUserId()).getIdentifier();
 
             String pkg = shortcutInfo.getPackage();
             long lastInteraction = mPeopleManager.getLastInteraction(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f56e6cd..dc5ba69 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -69,8 +69,10 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
         private const val DEFAULT_ALL_INDICATORS = false
         private const val DEFAULT_MIC_CAMERA = true
+        private const val DEFAULT_LOCATION = false
     }
 
     @VisibleForTesting
@@ -88,6 +90,11 @@
         return true
     }
 
+    private fun isLocationEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                LOCATION, DEFAULT_LOCATION)
+    }
+
     private var currentUserIds = emptyList<Int>()
     private var listening = false
     private val callbacks = mutableListOf<WeakReference<Callback>>()
@@ -107,13 +114,15 @@
         private set
     var micCameraAvailable = isMicCameraEnabled()
         private set
+    var locationAvailable = isLocationEnabled()
 
     private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
                     (properties.keyset.contains(ALL_INDICATORS) ||
-                    properties.keyset.contains(MIC_CAMERA))) {
+                            properties.keyset.contains(MIC_CAMERA) ||
+                            properties.keyset.contains(LOCATION))) {
 
                 // Running on the ui executor so can iterate on callbacks
                 if (properties.keyset.contains(ALL_INDICATORS)) {
@@ -126,6 +135,10 @@
 //                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
 //                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
 //                }
+                if (properties.keyset.contains(LOCATION)) {
+                    locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+                    callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
+                }
                 internalUiExecutor.updateListeningState()
             }
         }
@@ -139,7 +152,8 @@
             active: Boolean
         ) {
             // Check if we care about this code right now
-            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+            if (!allIndicatorsAvailable &&
+                    (code in OPS_LOCATION && !locationAvailable)) {
                 return
             }
             val userId = UserHandle.getUserId(uid)
@@ -195,7 +209,8 @@
      * main thread.
      */
     private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
+        val listen = !callbacks.isEmpty() and
+                (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -258,7 +273,9 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
-        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
+        if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+            return null
+        }
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
         return PrivacyItem(type, app)
     }
@@ -271,6 +288,9 @@
 
         @JvmDefault
         fun onFlagMicCameraChanged(flag: Boolean) {}
+
+        @JvmDefault
+        fun onFlagLocationChanged(flag: Boolean) {}
     }
 
     private class NotifyChangesToCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ca3e4cf..efe4609 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 import static com.android.systemui.util.Utils.useQsMediaPlayer;
 
@@ -158,7 +159,7 @@
             DumpManager dumpManager,
             BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
-            MediaHost mediaHost,
+            @Named(QS_PANEL) MediaHost mediaHost,
             UiEventLogger uiEventLogger,
             UserTracker userTracker
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index ea036f6..82cb863 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.content.Context;
@@ -70,7 +71,7 @@
             DumpManager dumpManager,
             BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
-            MediaHost mediaHost,
+            @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             UiEventLogger uiEventLogger,
             UserTracker userTracker
     ) {
@@ -108,6 +109,7 @@
     }
 
     @Override
+
     protected void initMediaHostState() {
         mMediaHost.setExpansion(0.0f);
         mMediaHost.setShowsOnlyActiveMedia(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 676a300..398edd7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -93,6 +93,7 @@
     private AlarmClockInfo mNextAlarm;
     private boolean mAllIndicatorsEnabled;
     private boolean mMicCameraIndicatorsEnabled;
+    private boolean mLocationIndicatorsEnabled;
     private boolean mPrivacyChipLogged;
     private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
 
@@ -156,6 +157,14 @@
             }
         }
 
+        @Override
+        public void onFlagLocationChanged(boolean flag) {
+            if (mLocationIndicatorsEnabled != flag) {
+                mLocationIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
         private void update() {
             StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons);
             iconContainer.setIgnoredSlots(getIgnoredIconSlots());
@@ -252,6 +261,7 @@
 
         mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
         mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+        mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
 
         setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
 
@@ -292,6 +302,7 @@
             // Get the most up to date info
             mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
             mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+            mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -323,7 +334,7 @@
                     com.android.internal.R.string.status_bar_camera));
             ignored.add(mView.getResources().getString(
                     com.android.internal.R.string.status_bar_microphone));
-            if (mAllIndicatorsEnabled) {
+            if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_location));
             }
@@ -333,7 +344,7 @@
     }
 
     private boolean getChipEnabled() {
-        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+        return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
     }
 
     private boolean isZenOverridingRinger() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8ff96c8..953de60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
@@ -38,7 +39,7 @@
  * Module for QS dependencies
  */
 // TODO: Add other QS classes
-@Module
+@Module(includes = {MediaModule.class})
 public interface QSModule {
 
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
deleted file mode 100644
index 1b1a51b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.service.notification.StatusBarNotification;
-import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.InfoMediaManager;
-import com.android.settingslib.media.LocalMediaManager;
-import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.widget.AdaptiveIcon;
-import com.android.systemui.Dependency;
-import com.android.systemui.media.dialog.MediaOutputDialogFactory;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for handling MediaTransfer state over a set of notifications.
- */
-public class MediaTransferManager {
-    private final Context mContext;
-    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
-    private MediaDevice mDevice;
-    private List<View> mViews = new ArrayList<>();
-    private LocalMediaManager mLocalMediaManager;
-
-    private static final String TAG = "MediaTransferManager";
-
-    private final View.OnClickListener mOnClickHandler = new View.OnClickListener() {
-        @Override
-        public void onClick(View view) {
-            if (handleMediaTransfer(view)) {
-                return;
-            }
-        }
-
-        private boolean handleMediaTransfer(View view) {
-            if (view.findViewById(com.android.internal.R.id.media_seamless) == null) {
-                return false;
-            }
-
-            ViewParent parent = view.getParent();
-            StatusBarNotification statusBarNotification =
-                    getRowForParent(parent).getEntry().getSbn();
-            mMediaOutputDialogFactory.create(statusBarNotification.getPackageName(), true);
-            return true;
-        }
-    };
-
-    private final LocalMediaManager.DeviceCallback mMediaDeviceCallback =
-            new LocalMediaManager.DeviceCallback() {
-        @Override
-        public void onDeviceListUpdate(List<MediaDevice> devices) {
-            MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice();
-            // Check because this can be called several times while changing devices
-            if (mDevice == null || !mDevice.equals(currentDevice)) {
-                mDevice = currentDevice;
-                updateAllChips();
-            }
-        }
-
-        @Override
-        public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
-            if (mDevice == null || !mDevice.equals(device)) {
-                mDevice = device;
-                updateAllChips();
-            }
-        }
-    };
-
-    public MediaTransferManager(Context context) {
-        mContext = context;
-        mMediaOutputDialogFactory = Dependency.get(MediaOutputDialogFactory.class);
-        LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class);
-        InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm);
-        mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null);
-    }
-
-    /**
-     * Mark a view as removed. If no views remain the media device listener will be unregistered.
-     * @param root
-     */
-    public void setRemoved(View root) {
-        if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER)
-                || mLocalMediaManager == null || root == null) {
-            return;
-        }
-        View view = root.findViewById(com.android.internal.R.id.media_seamless);
-        if (mViews.remove(view)) {
-            if (mViews.size() == 0) {
-                mLocalMediaManager.unregisterCallback(mMediaDeviceCallback);
-            }
-        } else {
-            Log.e(TAG, "Tried to remove unknown view " + view);
-        }
-    }
-
-    private ExpandableNotificationRow getRowForParent(ViewParent parent) {
-        while (parent != null) {
-            if (parent instanceof ExpandableNotificationRow) {
-                return ((ExpandableNotificationRow) parent);
-            }
-            parent = parent.getParent();
-        }
-        return null;
-    }
-
-    /**
-     * apply the action button for MediaTransfer
-     *
-     * @param root  The parent container of the view.
-     * @param entry The entry of MediaTransfer action button.
-     */
-    public void applyMediaTransferView(ViewGroup root, NotificationEntry entry) {
-        if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER)
-                || mLocalMediaManager == null || root == null) {
-            return;
-        }
-
-        View view = root.findViewById(com.android.internal.R.id.media_seamless);
-        if (view == null) {
-            return;
-        }
-
-        view.setVisibility(View.VISIBLE);
-        view.setOnClickListener(mOnClickHandler);
-        if (!mViews.contains(view)) {
-            mViews.add(view);
-            if (mViews.size() == 1) {
-                mLocalMediaManager.registerCallback(mMediaDeviceCallback);
-            }
-        }
-
-        // Initial update
-        mLocalMediaManager.startScan();
-        mDevice = mLocalMediaManager.getCurrentConnectedDevice();
-        updateChip(view);
-    }
-
-    private void updateAllChips() {
-        for (View view : mViews) {
-            updateChip(view);
-        }
-    }
-
-    private void updateChip(View view) {
-        ExpandableNotificationRow enr = getRowForParent(view.getParent());
-        int fgColor = enr.getNotificationHeader().getOriginalIconColor();
-        ColorStateList fgTintList = ColorStateList.valueOf(fgColor);
-        int bgColor = enr.getCurrentBackgroundTint();
-
-        // Update outline color
-        LinearLayout viewLayout = (LinearLayout) view;
-        RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
-        GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
-        rect.setStroke(2, fgColor);
-        rect.setColor(bgColor);
-
-        ImageView iconView = view.findViewById(com.android.internal.R.id.media_seamless_image);
-        TextView deviceName = view.findViewById(com.android.internal.R.id.media_seamless_text);
-        deviceName.setTextColor(fgTintList);
-
-        if (mDevice != null) {
-            Drawable icon = mDevice.getIcon();
-            iconView.setVisibility(View.VISIBLE);
-            iconView.setImageTintList(fgTintList);
-
-            if (icon instanceof AdaptiveIcon) {
-                AdaptiveIcon aIcon = (AdaptiveIcon) icon;
-                aIcon.setBackgroundColor(bgColor);
-                iconView.setImageDrawable(aIcon);
-            } else {
-                iconView.setImageDrawable(icon);
-            }
-            deviceName.setText(mDevice.getName());
-        } else {
-            // Reset to default
-            iconView.setVisibility(View.GONE);
-            deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8a644ed..3b55f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -44,7 +44,6 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.MediaTransferManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
@@ -173,12 +172,10 @@
     private boolean mIsContentExpandable;
     private boolean mRemoteInputVisible;
     private int mUnrestrictedContentHeight;
-    private MediaTransferManager mMediaTransferManager;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext());
-        mMediaTransferManager = new MediaTransferManager(getContext());
         mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
         mSmartReplyController = Dependency.get(SmartReplyController.class);
         initView();
@@ -1157,7 +1154,6 @@
             mHeadsUpWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
-        applyMediaTransfer(entry);
         updateLegacy();
         mForceSelectNextLayout = true;
         mPreviousExpandedRemoteInputIntent = null;
@@ -1185,22 +1181,6 @@
         }
     }
 
-    private void applyMediaTransfer(final NotificationEntry entry) {
-        if (!entry.isMediaNotification()) {
-            return;
-        }
-
-        View bigContentView = mExpandedChild;
-        if (bigContentView != null && (bigContentView instanceof ViewGroup)) {
-            mMediaTransferManager.applyMediaTransferView((ViewGroup) bigContentView, entry);
-        }
-
-        View smallContentView = mContractedChild;
-        if (smallContentView != null && (smallContentView instanceof ViewGroup)) {
-            mMediaTransferManager.applyMediaTransferView((ViewGroup) smallContentView, entry);
-        }
-    }
-
     /**
      * Returns whether the {@link Notification} represented by entry has a free-form remote input.
      * Such an input can be used e.g. to implement smart reply buttons - by passing the replies
@@ -1662,11 +1642,9 @@
         }
         if (mExpandedWrapper != null) {
             mExpandedWrapper.setRemoved();
-            mMediaTransferManager.setRemoved(mExpandedChild);
         }
         if (mContractedWrapper != null) {
             mContractedWrapper.setRemoved();
-            mMediaTransferManager.setRemoved(mContractedChild);
         }
         if (mHeadsUpWrapper != null) {
             mHeadsUpWrapper.setRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index e42c3dc..6ed092f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -671,14 +671,18 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+        if (mPrivacyItemController.getAllIndicatorsAvailable()
+                || mPrivacyItemController.getLocationAvailable()) {
             mIconController.setIconVisibility(mSlotLocation, showLocation);
         }
     }
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
+        if (!mPrivacyItemController.getAllIndicatorsAvailable()
+                && !mPrivacyItemController.getLocationAvailable()) {
+            updateLocationFromController();
+        }
     }
 
     // Updates the status view based on the current state of location requests.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 88a387d..f55c935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3987,7 +3987,8 @@
         PackageManager pm = mContext.getPackageManager();
         ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
         if (resolveInfo == null) {
-            Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
+            // TODO(b/171084088) Upgrade log to wtf when we have default app in main branch.
+            Log.d(TAG, "Couldn't find an app to process the emergency intent.");
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
index df741a0..89ab23b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
@@ -42,6 +42,12 @@
         TvGlobalRootComponent build();
     }
 
+    /**
+     * Builder for a WMComponent.
+     */
+    @Override
+    TvWMComponent.Builder getWMComponentBuilder();
+
     @Override
     TvSysUIComponent.Builder getSysUIComponent();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
index 334bb01..9621e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
@@ -19,7 +19,7 @@
 import dagger.Module;
 
 /**
- * Dagger module for including the WMComponent.
+ * Dagger module for including the SysUIComponent.
  */
 @Module(subcomponents = {TvSysUIComponent.class})
 public abstract class TvSysUIComponentModule {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index bde88b1..2c3ea4f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -22,7 +22,7 @@
 import dagger.Binds;
 import dagger.Module;
 
-@Module(includes = TvPipModule.class)
+@Module
 interface TvSystemUIBinder {
     @Binds
     GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index c5bb9c1..8ffc7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -62,7 +62,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.wmshell.TvWMShellModule;
 
 import javax.inject.Named;
 
@@ -75,8 +74,7 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            QSModule.class,
-            TvWMShellModule.class,
+            QSModule.class
         },
         subcomponents = {
         })
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
new file mode 100644
index 0000000..f678513
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.dagger.WMSingleton;
+import com.android.systemui.wmshell.TvWMShellModule;
+
+import dagger.Subcomponent;
+
+
+/**
+ * Dagger Subcomponent for WindowManager.
+ */
+@WMSingleton
+@Subcomponent(modules = {TvWMShellModule.class})
+public interface TvWMComponent extends WMComponent {
+
+    /**
+     * Builder for a SysUIComponent.
+     */
+    @Subcomponent.Builder
+    interface Builder extends WMComponent.Builder {
+        TvWMComponent build();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
index 66f8f74..6b5556b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
@@ -23,13 +23,19 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 
+import com.android.systemui.dagger.SysUISingleton;
+
 import java.util.concurrent.Executor;
 
+import javax.inject.Inject;
+
 /**
  * Wrapper around DeviceConfig useful for testing.
  */
+@SysUISingleton
 public class DeviceConfigProxy {
 
+    @Inject
     public DeviceConfigProxy() {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 28343ed..5310b3f 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.LayoutInflater;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
@@ -46,7 +46,7 @@
  */
 @Module
 public abstract class TvPipModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static Optional<Pip> providePip(
             Context context,
@@ -61,7 +61,7 @@
                         windowManagerShellWrapper));
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipControlsViewController providePipControlsViewController(
             PipControlsView pipControlsView, PipController pipController,
@@ -70,32 +70,33 @@
                 handler);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipControlsView providePipControlsView(Context context) {
         return new PipControlsView(context, null);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipNotification providePipNotification(Context context,
             PipController pipController) {
         return new PipNotification(context, pipController);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipBoundsHandler providePipBoundsHandler(Context context) {
-        return new PipBoundsHandler(context);
+    static PipBoundsHandler providePipBoundsHandler(Context context,
+            PipBoundsState pipBoundsState) {
+        return new PipBoundsHandler(context, pipBoundsState);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipBoundsState providePipBoundsState() {
         return new PipBoundsState();
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
             PipBoundsState pipBoundsState,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 7e1a2e8..294c749a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
@@ -40,10 +40,9 @@
  * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
  * branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module(includes = {WMShellBaseModule.class, TvPipModule.class})
 public class TvWMShellModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
             DisplayController displayController, @Main Executor mainExecutor,
@@ -52,7 +51,7 @@
                 transactionPool);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SplitScreen provideSplitScreen(Context context,
             DisplayController displayController, SystemWindows systemWindows,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 3c4c3fc..89ea9e2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -65,7 +65,6 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.nano.WmShellTraceProto;
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.onehanded.OneHandedEvents;
@@ -101,19 +100,18 @@
 
     private final CommandQueue mCommandQueue;
     private final ConfigurationController mConfigurationController;
-    private final DisplayImeController mDisplayImeController;
     private final InputConsumerController mInputConsumerController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final NavigationModeController mNavigationModeController;
     private final ScreenLifecycle mScreenLifecycle;
     private final SysUiState mSysUiState;
+    // TODO: This is only here because we need to dump state. Remove and replace with a dumper
+    //  interface.
+    private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<Pip> mPipOptional;
     private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<OneHanded> mOneHandedOptional;
-    // Inject the organizer directly in case the optionals aren't loaded to depend on it. There
-    // are non-optional windowing features like FULLSCREEN.
-    private final ShellTaskOrganizer mShellTaskOrganizer;
     private final ProtoTracer mProtoTracer;
     private boolean mIsSysUiStateValid;
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
@@ -126,7 +124,6 @@
             InputConsumerController inputConsumerController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             TaskStackChangeListeners taskStackChangeListeners,
-            DisplayImeController displayImeController,
             NavigationModeController navigationModeController,
             ScreenLifecycle screenLifecycle,
             SysUiState sysUiState,
@@ -141,7 +138,6 @@
         mInputConsumerController = inputConsumerController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTaskStackChangeListeners = taskStackChangeListeners;
-        mDisplayImeController = displayImeController;
         mNavigationModeController = navigationModeController;
         mScreenLifecycle = screenLifecycle;
         mSysUiState = sysUiState;
@@ -156,10 +152,6 @@
     @Override
     public void start() {
         mCommandQueue.addCallback(this);
-        // This is to prevent circular init problem by separating registration step out of its
-        // constructor. And make sure the initialization of DisplayImeController won't depend on
-        // specific feature anymore.
-        mDisplayImeController.startMonitorDisplays();
         mPipOptional.ifPresent(this::initPip);
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
@@ -201,6 +193,7 @@
             }
         });
 
+        // TODO: Move this into the shell
         // Handle for system task stack changes.
         mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
@@ -417,6 +410,11 @@
             return;
         }
         // Dump WMShell stuff here if no commands were handled
+        mShellTaskOrganizer.dump(pw, "");
+        pw.println();
+        pw.println();
+        mPipOptional.ifPresent(pip -> pip.dump(pw));
+        mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
         mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index ac6e5de..b333f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -20,21 +20,16 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.util.DisplayMetrics;
 import android.view.IWindowManager;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.bubbles.Bubbles;
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.common.AnimationThread;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
@@ -58,41 +53,28 @@
  * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here
  * should be shared among different branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module
 public abstract class WMShellBaseModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static TransactionPool provideTransactionPool() {
         return new TransactionPool();
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayController provideDisplayController(Context context, @Main Handler handler,
             IWindowManager wmService) {
         return new DisplayController(context, handler, wmService);
     }
 
-    @SysUISingleton
-    @Provides
-    static DeviceConfigProxy provideDeviceConfigProxy() {
-        return new DeviceConfigProxy();
-    }
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static InputConsumerController provideInputConsumerController() {
         return InputConsumerController.getPipInputConsumer();
     }
 
-    @SysUISingleton
-    @Provides
-    static FloatingContentCoordinator provideFloatingContentCoordinator() {
-        return new FloatingContentCoordinator();
-    }
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipAppOpsListener providePipAppOpsListener(Context context,
             IActivityManager activityManager,
@@ -100,61 +82,46 @@
         return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper());
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipMediaController providePipMediaController(Context context,
             IActivityManager activityManager) {
         return new PipMediaController(context, activityManager);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
             PackageManager packageManager) {
         return new PipUiEventLogger(uiEventLogger, packageManager);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
         return new PipSurfaceTransactionHelper(context);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SystemWindows provideSystemWindows(DisplayController displayController,
             IWindowManager wmService) {
         return new SystemWindows(displayController, wmService);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler,
             TransactionPool pool) {
         return new SyncTransactionQueue(pool, handler);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue,
             @Main Handler handler, TransactionPool transactionPool) {
-        ShellTaskOrganizer organizer = new ShellTaskOrganizer(syncQueue, transactionPool,
+        return new ShellTaskOrganizer(syncQueue, transactionPool,
                 new HandlerExecutor(handler), AnimationThread.instance().getExecutor());
-        organizer.registerOrganizer();
-        return organizer;
-    }
-
-    @SysUISingleton
-    @Provides
-    static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
-        return new WindowManagerShellWrapper();
-    }
-
-    @SysUISingleton
-    @Provides
-    static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
-            DisplayMetrics displayMetrics) {
-        return new FlingAnimationUtils.Builder(displayMetrics);
     }
 
     @BindsOptionalOf
@@ -163,7 +130,7 @@
     @BindsOptionalOf
     abstract Bubbles optionalBubbles();
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static Optional<OneHanded> provideOneHandedController(Context context,
             DisplayController displayController) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 81cb1f4..a6fe728 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -54,10 +54,9 @@
  * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
  * branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module(includes = WMShellBaseModule.class)
 public class WMShellModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
             DisplayController displayController, @Main Executor mainExecutor,
@@ -66,7 +65,7 @@
                 transactionPool);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SplitScreen provideSplitScreen(Context context,
             DisplayController displayController, SystemWindows systemWindows,
@@ -77,7 +76,7 @@
                 displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static Optional<Pip> providePip(Context context, DisplayController displayController,
             PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
@@ -90,26 +89,27 @@
                 windowManagerShellWrapper));
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipBoundsState providePipBoundsState() {
         return new PipBoundsState();
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipBoundsHandler providePipBoundsHandler(Context context) {
-        return new PipBoundsHandler(context);
+    static PipBoundsHandler providesPipBoundsHandler(Context context,
+            PipBoundsState pipBoundsState) {
+        return new PipBoundsHandler(context, pipBoundsState);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipMenuActivityController providePipMenuActivityController(Context context,
             PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) {
         return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipTouchHandler providePipTouchHandler(Context context,
             PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
@@ -121,7 +121,7 @@
                 pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
             PipBoundsState pipBoundsState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 86b1e61..9a77218 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -72,7 +72,7 @@
 
     @Before
     public void SysuiSetup() throws Exception {
-        SystemUIFactory.createFromConfig(mContext);
+        SystemUIFactory.createFromConfig(mContext, true);
         mDependency = new TestableDependency(
                 SystemUIFactory.getInstance().getSysUIComponent().createDependency());
         Dependency.setInstance(mDependency);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index b082d17..d9e9a8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,6 +45,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
 import android.os.Handler;
@@ -80,10 +83,8 @@
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -178,8 +179,6 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
-    private NotificationShelfComponent mNotificationShelfComponent;
-    @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
     private FeatureFlags mFeatureFlagsOldPipeline;
@@ -191,11 +190,12 @@
     private IStatusBarService mStatusBarService;
     @Mock
     private LauncherApps mLauncherApps;
-    @Mock private LockscreenLockIconController mLockIconController;
-
-    @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
-
-    @Mock private BubbleLogger mBubbleLogger;
+    @Mock
+    private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    @Mock
+    private BubbleLogger mBubbleLogger;
+    @Mock
+    private BubblePositioner mPositioner;
 
     private BubbleData mBubbleData;
 
@@ -210,7 +210,6 @@
         mContext.addMockSystemService(FaceManager.class, mFaceManager);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
-        // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -241,6 +240,13 @@
                 mSysUiStateBubblesExpanded =
                         (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
 
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
+
+        Rect availableRect = new Rect(0, 0, 1000, 5000);
+        when(mPositioner.getAvailableRect()).thenReturn(availableRect);
+        when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT);
+        when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0));
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -252,7 +258,6 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext, mBubbleLogger);
         when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -279,7 +284,8 @@
                 mLauncherApps,
                 mBubbleLogger,
                 mock(Handler.class),
-                mock(ShellTaskOrganizer.class));
+                mock(ShellTaskOrganizer.class),
+                mPositioner);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
         // Get a reference to the BubbleController's entry listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
deleted file mode 100644
index e7bf86e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 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.bubbles;
-
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.SurfaceControl;
-import android.window.WindowContainerToken;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.wm.shell.ShellTaskOrganizer;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
-public class MultiWindowTaskListenerTest extends SysuiTestCase {
-
-    @Mock
-    ShellTaskOrganizer mOrganizer;
-    @Mock
-    MultiWindowTaskListener.Listener mPendingListener;
-    @Mock
-    SurfaceControl mLeash;
-    @Mock
-    ActivityManager.RunningTaskInfo mTaskInfo;
-    @Mock
-    WindowContainerToken mToken;
-
-    Handler mHandler;
-    MultiWindowTaskListener mTaskListener;
-    TestableLooper mTestableLooper;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mTestableLooper = TestableLooper.get(this);
-        mHandler = new Handler(mTestableLooper.getLooper());
-
-        mTaskInfo = new ActivityManager.RunningTaskInfo();
-        mTaskInfo.token = mToken;
-
-        mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
-    }
-
-    private void addTaskAndVerify() {
-        final Binder cookie = new Binder();
-        mTaskInfo.addLaunchCookie(cookie);
-        mTaskListener.setPendingLaunchCookieListener(cookie, mPendingListener);
-        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
-        mTestableLooper.processAllMessages();
-        verify(mPendingListener).onTaskAppeared(eq(mTaskInfo), eq(mLeash));
-    }
-
-    @Test
-    public void testListenForMultiWindowMode() {
-        mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
-        verify(mOrganizer).addListener(eq(mTaskListener), eq(TASK_LISTENER_TYPE_MULTI_WINDOW));
-    }
-
-    @Test
-    public void testRemovePendingListener() {
-        addTaskAndVerify();
-        reset(mPendingListener);
-
-        mTaskListener.removeListener(mPendingListener);
-
-        // If it was removed, our pendingListener shouldn't get triggered:
-        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
-        mTaskListener.onTaskInfoChanged(mTaskInfo);
-        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
-        mTaskListener.onTaskVanished(mTaskInfo);
-
-        mTestableLooper.processAllMessages();
-        verify(mPendingListener, never()).onTaskAppeared(any(), any());
-        verify(mPendingListener, never()).onTaskInfoChanged(any());
-        verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
-        verify(mPendingListener, never()).onTaskVanished(any());
-    }
-
-    @Test
-    public void testOnTaskAppeared() {
-        addTaskAndVerify();
-        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mToken), eq(true));
-    }
-
-    @Test
-    public void testOnTaskAppeared_nullListener() {
-        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
-        mTestableLooper.processAllMessages();
-
-        verify(mOrganizer, never()).setInterceptBackPressedOnTaskRoot(any(), anyBoolean());
-        verify(mPendingListener, never()).onTaskAppeared(any(), any());
-    }
-
-    @Test
-    public void testOnTaskVanished() {
-        addTaskAndVerify();
-        mTaskListener.onTaskVanished(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener).onTaskVanished(eq(mTaskInfo));
-    }
-
-    @Test
-    public void testOnTaskVanished_neverAdded() {
-        mTaskListener.onTaskVanished(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener, never()).onTaskVanished(any());
-    }
-
-    @Test
-    public void testOnTaskInfoChanged() {
-        addTaskAndVerify();
-        mTaskListener.onTaskInfoChanged(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener).onTaskInfoChanged(eq(mTaskInfo));
-    }
-
-    @Test
-    public void testOnTaskInfoChanged_neverAdded() {
-        mTaskListener.onTaskInfoChanged(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener, never()).onTaskInfoChanged(any());
-    }
-
-    @Test
-    public void testOnBackPressedOnTaskRoot() {
-        addTaskAndVerify();
-        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener).onBackPressedOnTaskRoot(eq(mTaskInfo));
-    }
-
-    @Test
-    public void testOnBackPressedOnTaskRoot_neverAdded() {
-        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
-        mTestableLooper.processAllMessages();
-
-        verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index cbacd53..b9394ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -42,7 +42,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.pm.LauncherApps;
-import android.content.res.Resources;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
 import android.os.Handler;
@@ -170,8 +172,6 @@
     @Mock
     ColorExtractor.GradientColors mGradientColors;
     @Mock
-    private Resources mResources;
-    @Mock
     private ShadeController mShadeController;
     @Mock
     private NotificationShelfComponent mNotificationShelfComponent;
@@ -191,6 +191,8 @@
     private WindowManagerShellWrapper mWindowManagerShellWrapper;
     @Mock
     private BubbleLogger mBubbleLogger;
+    @Mock
+    private BubblePositioner mPositioner;
 
     private BubbleData mBubbleData;
 
@@ -223,7 +225,6 @@
                 },
                 mLockIconController);
 
-        // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -243,6 +244,13 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
+
+        Rect availableRect = new Rect(0, 0, 1000, 5000);
+        when(mPositioner.getAvailableRect()).thenReturn(availableRect);
+        when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT);
+        when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0));
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -254,7 +262,6 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext, mBubbleLogger);
         when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -281,7 +288,8 @@
                 mLauncherApps,
                 mBubbleLogger,
                 mock(Handler.class),
-                mock(ShellTaskOrganizer.class));
+                mock(ShellTaskOrganizer.class),
+                mPositioner);
         mBubbleController.addNotifCallback(mNotifCallback);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
index 63d60f9..b13c6fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -35,6 +36,7 @@
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.SurfaceControl;
@@ -46,6 +48,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.HandlerExecutor;
 
 import org.junit.After;
 import org.junit.Before;
@@ -53,6 +56,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -69,7 +73,7 @@
     @Mock
     ShellTaskOrganizer mOrganizer;
     @Mock
-    MultiWindowTaskListener mTaskListener;
+    HandlerExecutor mExecutor;
 
     SurfaceSession mSession;
     SurfaceControl mLeash;
@@ -86,13 +90,18 @@
 
         mContext = getContext();
 
-        when(mTaskListener.getTaskOrganizer()).thenReturn(mOrganizer);
         mTaskInfo = new ActivityManager.RunningTaskInfo();
         mTaskInfo.token = mToken;
         mTaskInfo.taskId = 314;
         mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
 
-        mTaskView = new TaskView(mContext, mTaskListener);
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable r = invocationOnMock.getArgument(0);
+            r.run();
+            return null;
+        }).when(mExecutor).execute(any());
+
+        mTaskView = new TaskView(mContext, mOrganizer, mExecutor);
         mTaskView.setListener(mViewListener);
     }
 
@@ -105,7 +114,7 @@
 
     @Test
     public void testSetPendingListener_throwsException() {
-        TaskView taskView = new TaskView(mContext, mTaskListener);
+        TaskView taskView = new TaskView(mContext, mOrganizer, mExecutor);
         taskView.setListener(mViewListener);
         try {
             taskView.setListener(mViewListener);
@@ -121,7 +130,7 @@
         ActivityOptions options = ActivityOptions.makeBasic();
         mTaskView.startActivity(mock(PendingIntent.class), null, options);
 
-        verify(mTaskListener).setPendingLaunchCookieListener(any(), eq(mTaskView));
+        verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskView));
         assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
         assertThat(options.getTaskAlwaysOnTop()).isTrue();
     }
@@ -189,7 +198,7 @@
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
         mTaskView.release();
 
-        verify(mTaskListener).removeListener(eq(mTaskView));
+        verify(mOrganizer).removeListener(eq(mTaskView));
         verify(mViewListener).onReleased();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 27c6fc1..aaeee16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -70,7 +70,8 @@
             LauncherApps launcherApps,
             BubbleLogger bubbleLogger,
             Handler mainHandler,
-            ShellTaskOrganizer shellTaskOrganizer) {
+            ShellTaskOrganizer shellTaskOrganizer,
+            BubblePositioner positioner) {
         super(context,
                 notificationShadeWindowController, statusBarStateController, shadeController,
                 data, Runnable::run, configurationController, interruptionStateProvider,
@@ -78,7 +79,7 @@
                 notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
                 dataRepository, sysUiState, notificationManager, statusBarService,
                 windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
-                mainHandler, shellTaskOrganizer);
+                mainHandler, shellTaskOrganizer, positioner);
         setInflateSynchronously(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index 6a14863..a5bb8ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -17,26 +17,30 @@
 package com.android.systemui.bubbles.animation;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.SuppressLint;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Point;
+import android.graphics.Insets;
 import android.graphics.PointF;
+import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 import org.mockito.Spy;
 
 @SmallTest
@@ -46,47 +50,45 @@
     private int mDisplayWidth = 500;
     private int mDisplayHeight = 1000;
     private int mExpandedViewPadding = 10;
-    private int mOrientation = Configuration.ORIENTATION_PORTRAIT;
-    private float mLauncherGridDiff = 30f;
 
-    private Runnable mOnBubbleAnimatedOutAction = Mockito.mock(Runnable.class);
-
+    private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
     @Spy
-    private ExpandedAnimationController mExpandedController =
-            new ExpandedAnimationController(
-                    new Point(mDisplayWidth, mDisplayHeight) /* displaySize */,
-                    mExpandedViewPadding, mOrientation, mOnBubbleAnimatedOutAction);
+    ExpandedAnimationController mExpandedController;
 
     private int mStackOffset;
-    private float mBubblePaddingTop;
-    private float mBubbleSize;
-
     private PointF mExpansionPoint;
 
+    @SuppressLint("VisibleForTests")
     @Before
     public void setUp() throws Exception {
         super.setUp();
+
+        BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+        positioner.update(Configuration.ORIENTATION_PORTRAIT,
+                Insets.of(0, 0, 0, 0),
+                new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding,
+                mOnBubbleAnimatedOutAction);
+
         addOneMoreThanBubbleLimitBubbles();
         mLayout.setActiveController(mExpandedController);
 
         Resources res = mLayout.getResources();
         mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
-        mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-        mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mExpansionPoint = new PointF(100, 100);
     }
 
     @Test
     @Ignore
     public void testExpansionAndCollapse() throws InterruptedException {
-        Runnable afterExpand = Mockito.mock(Runnable.class);
+        Runnable afterExpand = mock(Runnable.class);
         mExpandedController.expandFromStack(afterExpand);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
         testBubblesInCorrectExpandedPositions();
         verify(afterExpand).run();
 
-        Runnable afterCollapse = Mockito.mock(Runnable.class);
+        Runnable afterCollapse = mock(Runnable.class);
         mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
@@ -121,7 +123,7 @@
 
     /** Expand the stack and wait for animations to finish. */
     private void expand() throws InterruptedException {
-        mExpandedController.expandFromStack(Mockito.mock(Runnable.class));
+        mExpandedController.expandFromStack(mock(Runnable.class));
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
     }
 
@@ -141,51 +143,12 @@
     private void testBubblesInCorrectExpandedPositions() {
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < mLayout.getChildCount(); i++) {
-            assertEquals(getBubbleLeft(i),
+            float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i);
+            assertEquals(expectedPosition,
                     mLayout.getChildAt(i).getTranslationX(),
                     2f);
-            assertEquals(mExpandedController.getExpandedY(),
+            assertEquals(expectedPosition,
                     mLayout.getChildAt(i).getTranslationY(), 2f);
         }
     }
-
-    /**
-     * @param index Bubble index in row.
-     * @return Bubble left x from left edge of screen.
-     */
-    public float getBubbleLeft(int index) {
-        final float bubbleLeft = index * (mBubbleSize + getSpaceBetweenBubbles());
-        return getRowLeft() + bubbleLeft;
-    }
-
-    private float getRowLeft() {
-        if (mLayout == null) {
-            return 0;
-        }
-        int bubbleCount = mLayout.getChildCount();
-        final float totalBubbleWidth = bubbleCount * mBubbleSize;
-        final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
-        final float rowWidth = totalGapWidth + totalBubbleWidth;
-
-        final float centerScreen = mDisplayWidth / 2f;
-        final float halfRow = rowWidth / 2f;
-        final float rowLeft = centerScreen - halfRow;
-
-        return rowLeft;
-    }
-
-    /**
-     * @return Space between bubbles in row above expanded view.
-     */
-    private float getSpaceBetweenBubbles() {
-        final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2;
-        final float maxRowWidth = mDisplayWidth - rowMargins;
-
-        final float totalBubbleWidth = mMaxBubbles * mBubbleSize;
-        final float totalGapWidth = maxRowWidth - totalBubbleWidth;
-
-        final int gapCount = mMaxBubbles - 1;
-        final float gapWidth = totalGapWidth / gapCount;
-        return gapWidth;
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index 9242ce9..7d0abec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -33,6 +34,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
 import org.junit.Before;
@@ -40,7 +42,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -67,7 +68,7 @@
                     public int getAsInt() {
                         return mLayout.getChildCount();
                     }
-                }, Mockito.mock(Runnable.class)));
+                }, mock(Runnable.class)));
         mLayout.setActiveController(mStackController);
         addOneMoreThanBubbleLimitBubbles();
         mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -306,7 +307,10 @@
                 FloatingContentCoordinator floatingContentCoordinator,
                 IntSupplier bubbleCountSupplier,
                 Runnable onBubbleAnimatedOutAction) {
-            super(floatingContentCoordinator, bubbleCountSupplier, onBubbleAnimatedOutAction);
+            super(floatingContentCoordinator,
+                    bubbleCountSupplier,
+                    onBubbleAnimatedOutAction,
+                    mock(BubblePositioner.class));
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
index 58959c4..bfc7935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.globalactions;
 
+import static android.provider.Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD;
 import static android.view.WindowInsets.Type.ime;
 
 import static org.junit.Assert.assertEquals;
@@ -24,9 +25,11 @@
 import static org.junit.Assert.fail;
 
 import android.app.Activity;
+import android.content.ContentResolver;
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowInsetsController;
@@ -41,6 +44,7 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -54,8 +58,23 @@
     public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(
             TestActivity.class, false, false);
 
+    private int mOriginalShowImeWithHardKeyboard;
+
+    @Before
+    public void setUp() {
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        mOriginalShowImeWithHardKeyboard = Settings.Secure.getInt(
+                contentResolver, SHOW_IME_WITH_HARD_KEYBOARD, 0);
+        // Forcibly shows IME even when hardware keyboard is connected.
+        // To change USER_SYSTEM settings, we have to use settings shell command.
+        executeShellCommand("settings put secure " + SHOW_IME_WITH_HARD_KEYBOARD + " 1");
+    }
+
     @After
     public void tearDown() {
+        // To restore USER_SYSTEM settings, we have to use settings shell command.
+        executeShellCommand("settings put secure "
+                + SHOW_IME_WITH_HARD_KEYBOARD + " " + mOriginalShowImeWithHardKeyboard);
         executeShellCommand("input keyevent HOME");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index b81ab74..1f9862c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -654,4 +654,21 @@
         fakeExecutor.runAllReady()
         verify(mockController).unregisterCallback(any())
     }
+
+    @Test
+    fun nullPlaybackStateUnregistersCallback() {
+        viewModel.updateController(mockController)
+        val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+        verify(mockController).registerCallback(captor.capture())
+        val callback = captor.value
+        // WHEN the callback receives a null state
+        callback.onPlaybackStateChanged(null)
+        with(fakeExecutor) {
+            advanceClockToNext()
+            runAllReady()
+        }
+        // THEN we unregister callback (as a result of clearing the controller)
+        fakeExecutor.runAllReady()
+        verify(mockController).unregisterCallback(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index cb17829..cd94f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -54,6 +54,7 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
     }
 
     @Mock
@@ -116,6 +117,15 @@
     }
 
     @Test
+    fun testLocationChanged() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagLocationChanged(true)
+        assertTrue(privacyItemController.locationAvailable)
+    }
+
+    @Test
     fun testAllChanged() {
         changeAll(true)
         executor.runAllReady()
@@ -158,6 +168,14 @@
     }
 
     @Test
+    fun testLocation_listening() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
     @Ignore // TODO(b/168209929)
     fun testAllFalse_notListening() {
         changeAll(true)
@@ -205,6 +223,7 @@
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
     private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
 
     private fun changeProperty(name: String, value: Boolean?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index a58f1fd..dd7f263 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -27,6 +27,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
@@ -53,6 +54,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
+import java.util.concurrent.ExecutionException;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -64,7 +66,6 @@
     @Mock ConfigurationController mConfigurationController;
     @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock TaskStackChangeListeners mTaskStackChangeListeners;
-    @Mock DisplayImeController mDisplayImeController;
     @Mock InputConsumerController mMockInputConsumerController;
     @Mock NavigationModeController mNavigationModeController;
     @Mock ScreenLifecycle mScreenLifecycle;
@@ -84,21 +85,13 @@
 
         mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
                 mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners,
-                mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
-                Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
-                mTaskOrganizer, mProtoTracer);
+                mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip),
+                Optional.of(mSplitScreen), Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
 
         when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
     }
 
     @Test
-    public void start_startsMonitorDisplays() {
-        mWMShell.start();
-
-        verify(mDisplayImeController).startMonitorDisplays();
-    }
-
-    @Test
     public void initPip_registersCommandQueueCallback() {
         mWMShell.initPip(mPip);
 
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 9fc1d7e..4f616cd 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -80,11 +80,6 @@
     private final SparseArray<LinkAddress> mCachedAddresses;
 
     public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
-        this(context, config, new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))));
-    }
-
-    public PrivateAddressCoordinator(Context context, TetheringConfiguration config,
-            List<IpPrefix> prefixPools) {
         mDownstreams = new ArraySet<>();
         mUpstreamPrefixMap = new ArrayMap<>();
         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
@@ -95,7 +90,11 @@
         mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
         mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
 
-        mTetheringPrefixes = prefixPools;
+        mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")));
+        if (config.isSelectAllPrefixRangeEnabled()) {
+            mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12"));
+            mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8"));
+        }
     }
 
     /**
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 5783805..799637c 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -40,7 +40,6 @@
 import java.util.Collection;
 import java.util.StringJoiner;
 
-
 /**
  * A utility class to encapsulate the various tethering configuration elements.
  *
@@ -88,6 +87,13 @@
             "use_legacy_wifi_p2p_dedicated_ip";
 
     /**
+     * Flag use to enable select all prefix ranges feature.
+     * TODO: Remove this flag if there are no problems after M-2020-12 rolls out.
+     */
+    public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
+            "tether_enable_select_all_prefix_ranges";
+
+    /**
      * Default value that used to periodic polls tether offload stats from tethering offload HAL
      * to make the data warnings work.
      */
@@ -118,6 +124,8 @@
     private final boolean mEnableBpfOffload;
     private final boolean mEnableWifiP2pDedicatedIp;
 
+    private final boolean mEnableSelectAllPrefixRange;
+
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
 
@@ -164,6 +172,11 @@
                 R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
                 false /* defaultValue */);
 
+        // Flags should normally not be booleans, but this is a kill-switch flag that is only used
+        // to turn off the feature, so binary rollback problems do not apply.
+        mEnableSelectAllPrefixRange = getDeviceConfigBoolean(
+                TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */);
+
         configLog.log(toString());
     }
 
@@ -249,6 +262,9 @@
 
         pw.print("enableWifiP2pDedicatedIp: ");
         pw.println(mEnableWifiP2pDedicatedIp);
+
+        pw.print("mEnableSelectAllPrefixRange: ");
+        pw.println(mEnableSelectAllPrefixRange);
     }
 
     /** Returns the string representation of this object.*/
@@ -310,6 +326,10 @@
         return mEnableBpfOffload;
     }
 
+    public boolean isSelectAllPrefixRangeEnabled() {
+        return mEnableSelectAllPrefixRange;
+    }
+
     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
         final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp
new file mode 100644
index 0000000..f925b0a
--- /dev/null
+++ b/packages/Tethering/tests/mts/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2020 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.
+
+android_test {
+    // This tests for functionality that is not required for devices that
+    // don't use Tethering mainline module.
+    name: "MtsTetheringTest",
+
+    libs: [
+        "android.test.base",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        // mockito-target-extended-minus-junit4 used in this lib have dependency with
+        // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent.
+        "cts-net-utils",
+        // This is needed for androidx.test.runner.AndroidJUnitRunner.
+        "ctstestrunner-axt",
+        "junit",
+        "junit-params",
+    ],
+
+    jni_libs: [
+        // For mockito extended which is pulled in from -net-utils -> net-tests-utils
+        // (mockito-target-extended-minus-junit4).
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    platform_apis: true,
+
+    // Tag this module as a mts test artifact
+    test_suites: [
+        "general-tests",
+        "mts",
+    ],
+
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+}
diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml
new file mode 100644
index 0000000..6d2abca
--- /dev/null
+++ b/packages/Tethering/tests/mts/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.tethering.mts">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.tethering.mts"
+                     android:label="MTS tests of android.tethering">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml
new file mode 100644
index 0000000..80788df
--- /dev/null
+++ b/packages/Tethering/tests/mts/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for MTS Tethering test cases">
+    <option name="test-suite-tag" value="mts" />
+    <option name="config-descriptor:metadata" key="component" value="networking" />
+    <!-- Instant app do not have INTERNET permission. -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <!-- Feature is not backed by native code. -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <!-- Allow running this against a secondary user. -->
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="MtsTetheringTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.tethering.mts" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.tethering" />
+    </object>
+</configuration>
diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
new file mode 100644
index 0000000..7ffe37a
--- /dev/null
+++ b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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.tethering.mts;
+
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.TetheringManager;
+import android.net.cts.util.CtsTetheringUtils;
+import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TestNetworkTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class TetheringModuleTest {
+    private Context mContext;
+    private TetheringManager mTm;
+    private CtsTetheringUtils mCtsTetheringUtils;
+
+    private UiAutomation mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    @Before
+    public void setUp() throws Exception {
+        mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
+                WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED);
+        mContext = InstrumentationRegistry.getContext();
+        mTm = mContext.getSystemService(TetheringManager.class);
+        mCtsTetheringUtils = new CtsTetheringUtils(mContext);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiAutomation.dropShellPermissionIdentity();
+    }
+
+    private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
+            "tether_enable_select_all_prefix_ranges";
+    @Test
+    public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
+        assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true));
+
+        addressConflictTest(true);
+    }
+
+    @Test
+    public void testSwitchPrefixRangeWhenConflict() throws Exception {
+        addressConflictTest(false);
+    }
+
+    private void addressConflictTest(final boolean wholeRangeConflict) throws Exception {
+        final TestTetheringEventCallback tetherEventCallback =
+                mCtsTetheringUtils.registerTetheringEventCallback();
+
+        TestNetworkTracker tnt = null;
+        try {
+            tetherEventCallback.assumeTetheringSupported();
+            assumeTrue(isWifiTetheringSupported(tetherEventCallback));
+
+            mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+
+            final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
+            assertEquals(1, tetheredIfaces.size());
+            final String wifiTetheringIface = tetheredIfaces.get(0);
+
+            NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
+            // Tethering downstream only have one ipv4 address.
+            final LinkAddress hotspotAddr = getFirstIpv4Address(nif);
+            assertNotNull(hotspotAddr);
+
+            final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict);
+            assertNotNull(testPrefix);
+
+            tnt = setUpTestNetwork(
+                    new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
+
+            tetherEventCallback.expectTetheredInterfacesChanged(null);
+            final List<String> wifiRegexs =
+                    tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
+
+            tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
+            nif = NetworkInterface.getByName(wifiTetheringIface);
+            final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
+            assertNotNull(newHotspotAddr);
+
+            assertFalse(testPrefix.containsPrefix(
+                    new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength())));
+
+            mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
+        } finally {
+            if (tnt != null) {
+                tnt.teardown();
+            }
+            mTm.stopAllTethering();
+            mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+        }
+    }
+
+    private LinkAddress getFirstIpv4Address(final NetworkInterface nif) {
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (addr.isIpv4()) return addr;
+        }
+        return null;
+    }
+
+    @NonNull
+    private IpPrefix getConflictingPrefix(final LinkAddress address,
+            final boolean wholeRangeConflict) {
+        if (!wholeRangeConflict) {
+            return new IpPrefix(address.getAddress(), address.getPrefixLength());
+        }
+
+        final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
+                new IpPrefix("192.168.0.0/16"),
+                new IpPrefix("172.16.0.0/12"),
+                new IpPrefix("10.0.0.0/8")));
+
+        for (IpPrefix prefix : prefixPool) {
+            if (prefix.contains(address.getAddress())) return prefix;
+        }
+
+        fail("Could not find sutiable conflict prefix");
+
+        // Never go here.
+        return null;
+    }
+
+    private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception {
+        return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/);
+
+    }
+
+    public static boolean isFeatureEnabled(final String name, final boolean defaultValue) {
+        return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 8cb80ba..41d46e5 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -99,9 +99,9 @@
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
         when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
+        when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true);
         setUpIpServers();
-        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig,
-                mTetheringPrefixes));
+        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
     }
 
     private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index dc0940c..237e2c2 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -131,6 +131,7 @@
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
                 .thenReturn(false);
         initializeBpfOffloadConfiguration(true, null /* unset */);
+        initEnableSelectAllPrefixRangeFlag(null /* unset */);
 
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
@@ -428,4 +429,30 @@
                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
     }
+
+    private void initEnableSelectAllPrefixRangeFlag(final String value) {
+        doReturn(value).when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES)));
+    }
+
+    @Test
+    public void testSelectAllPrefixRangeFlag() throws Exception {
+        // Test default value.
+        final TetheringConfiguration defaultCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled());
+
+        // Test disable flag.
+        initEnableSelectAllPrefixRangeFlag("false");
+        final TetheringConfiguration testDisable = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(testDisable.isSelectAllPrefixRangeEnabled());
+
+        // Test enable flag.
+        initEnableSelectAllPrefixRangeFlag("true");
+        final TetheringConfiguration testEnable = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(testEnable.isSelectAllPrefixRangeEnabled());
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 20e94b2..114cb7c 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -456,11 +456,7 @@
         @Override
         public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
                 TetheringConfiguration cfg) {
-            final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
-                    new IpPrefix("192.168.0.0/16"),
-                    new IpPrefix("172.16.0.0/12"),
-                    new IpPrefix("10.0.0.0/8")));
-            mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(ctx, cfg, prefixPool));
+            mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
             return mPrivateAddressCoordinator;
         }
     }
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 72a2152..43c54b4 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1106,17 +1106,4 @@
         }
     }
 
-    /**
-     * Retrieve all of the information we know about a particular activity class including its
-     * package states.
-     *
-     * @param packageName a specific package
-     * @param filterCallingUid The results will be filtered in the context of this UID instead
-     *                         of the calling UID.
-     * @param userId The user for whom the package is installed
-     * @return IncrementalStatesInfo that contains information about package states.
-     */
-    public abstract IncrementalStatesInfo getIncrementalStatesInfo(String packageName,
-            int filterCallingUid, int userId);
-
 }
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 31cd5d5..4d9680c 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -16,22 +16,14 @@
 
 package com.android.server;
 
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.Context;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
 import android.util.Log;
-import android.widget.Toast;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
@@ -53,7 +45,7 @@
 
     private final BluetoothManagerService mBluetoothManager;
     private final BluetoothAirplaneModeHandler mHandler;
-    private AirplaneModeHelper mAirplaneHelper;
+    private BluetoothModeChangeHelper mAirplaneHelper;
 
     @VisibleForTesting int mToastCount = 0;
 
@@ -97,7 +89,7 @@
      * Call after boot complete
      */
     @VisibleForTesting
-    void start(AirplaneModeHelper helper) {
+    void start(BluetoothModeChangeHelper helper) {
         Log.i(TAG, "start");
         mAirplaneHelper = helper;
         mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
@@ -141,118 +133,4 @@
         }
         return true;
     }
-
-    /**
-     * Helper class that handles callout and callback methods without
-     * complex logic.
-     */
-    @VisibleForTesting
-    public static class AirplaneModeHelper {
-        private volatile BluetoothA2dp mA2dp;
-        private volatile BluetoothHearingAid mHearingAid;
-        private final BluetoothAdapter mAdapter;
-        private final Context mContext;
-
-        AirplaneModeHelper(Context context) {
-            mAdapter = BluetoothAdapter.getDefaultAdapter();
-            mContext = context;
-
-            mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
-            mAdapter.getProfileProxy(mContext, mProfileServiceListener,
-                    BluetoothProfile.HEARING_AID);
-        }
-
-        private final ServiceListener mProfileServiceListener = new ServiceListener() {
-            @Override
-            public void onServiceConnected(int profile, BluetoothProfile proxy) {
-                // Setup Bluetooth profile proxies
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = (BluetoothA2dp) proxy;
-                        break;
-                    case BluetoothProfile.HEARING_AID:
-                        mHearingAid = (BluetoothHearingAid) proxy;
-                        break;
-                    default:
-                        break;
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(int profile) {
-                // Clear Bluetooth profile proxies
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = null;
-                        break;
-                    case BluetoothProfile.HEARING_AID:
-                        mHearingAid = null;
-                        break;
-                    default:
-                        break;
-                }
-            }
-        };
-
-        @VisibleForTesting
-        public boolean isA2dpOrHearingAidConnected() {
-            return isA2dpConnected() || isHearingAidConnected();
-        }
-
-        @VisibleForTesting
-        public boolean isBluetoothOn() {
-            final BluetoothAdapter adapter = mAdapter;
-            if (adapter == null) {
-                return false;
-            }
-            return adapter.getLeState() == BluetoothAdapter.STATE_ON;
-        }
-
-        @VisibleForTesting
-        public boolean isAirplaneModeOn() {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-        }
-
-        @VisibleForTesting
-        public void onAirplaneModeChanged(BluetoothManagerService managerService) {
-            managerService.onAirplaneModeChanged();
-        }
-
-        @VisibleForTesting
-        public int getSettingsInt(String name) {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    name, 0);
-        }
-
-        @VisibleForTesting
-        public void setSettingsInt(String name, int value) {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    name, value);
-        }
-
-        @VisibleForTesting
-        public void showToastMessage() {
-            Resources r = mContext.getResources();
-            final CharSequence text = r.getString(
-                    R.string.bluetooth_airplane_mode_toast, 0);
-            Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
-        }
-
-        private boolean isA2dpConnected() {
-            final BluetoothA2dp a2dp = mA2dp;
-            if (a2dp == null) {
-                return false;
-            }
-            return a2dp.getConnectedDevices().size() > 0;
-        }
-
-        private boolean isHearingAidConnected() {
-            final BluetoothHearingAid hearingAid = mHearingAid;
-            if (hearingAid == null) {
-                return false;
-            }
-            return hearingAid.getConnectedDevices().size() > 0;
-        }
-    };
 }
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
new file mode 100644
index 0000000..2dcf82f
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 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;
+
+import android.provider.DeviceConfig;
+
+/**
+ * The BluetoothDeviceConfigListener handles system device config change callback and checks
+ * whether we need to inform BluetoothManagerService on this change.
+ *
+ * The information of device config change would not be passed to the BluetoothManagerService
+ * when Bluetooth is on and Bluetooth is in one of the following situations:
+ *   1. Bluetooth A2DP is connected.
+ *   2. Bluetooth Hearing Aid profile is connected.
+ */
+class BluetoothDeviceConfigListener {
+    private static final String TAG = "BluetoothDeviceConfigListener";
+
+    BluetoothManagerService mService;
+
+    BluetoothDeviceConfigListener(BluetoothManagerService service) {
+        mService = service;
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_BLUETOOTH,
+                (Runnable r) -> r.run(),
+                mDeviceConfigChangedListener);
+    }
+
+    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(DeviceConfig.Properties properties) {
+                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
+                        return;
+                    }
+                    boolean foundInit = false;
+                    for (String name : properties.getKeyset()) {
+                        if (name.startsWith("INIT_")) {
+                            foundInit = true;
+                            break;
+                        }
+                    }
+                    if (!foundInit) {
+                        return;
+                    }
+                    mService.onInitFlagsChanged();
+                }
+            };
+
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 011231c..f6a29aa 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -23,7 +23,9 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.IBluetooth;
@@ -63,7 +65,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
@@ -118,6 +119,7 @@
     // Delay for retrying enable and disable in msec
     private static final int ENABLE_DISABLE_DELAY_MS = 300;
     private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
+    private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
@@ -178,8 +180,12 @@
     private int mWaitForEnableRetry;
     private int mWaitForDisableRetry;
 
+    private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
+
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
 
+    private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
+
     // used inside handler thread
     private boolean mQuietEnable = false;
     private boolean mEnable;
@@ -284,29 +290,13 @@
                 }
             };
 
-    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
-                        return;
-                    }
-                    boolean foundInit = false;
-                    for (String name : properties.getKeyset()) {
-                        if (name.startsWith("INIT_")) {
-                            foundInit = true;
-                            break;
-                        }
-                    }
-                    if (!foundInit) {
-                        return;
-                    }
-                    mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-                    mHandler.sendEmptyMessageDelayed(
-                            MESSAGE_INIT_FLAGS_CHANGED,
-                            DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
-                }
-            };
+    @VisibleForTesting
+    public void onInitFlagsChanged() {
+        mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+        mHandler.sendEmptyMessageDelayed(
+                MESSAGE_INIT_FLAGS_CHANGED,
+                DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
+    }
 
     public boolean onFactoryReset() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
@@ -457,6 +447,15 @@
                         mHandler.sendMessage(msg);
                     }
                 }
+            } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
+                    || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+                final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                        BluetoothProfile.STATE_CONNECTED);
+                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
+                        && state == BluetoothProfile.STATE_DISCONNECTED
+                        && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                    onInitFlagsChanged();
+                }
             }
         }
     };
@@ -508,6 +507,8 @@
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
         filter.addAction(Intent.ACTION_SETTING_RESTORED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiver(mReceiver, filter);
 
@@ -542,10 +543,6 @@
             Slog.w(TAG, "Unable to resolve SystemUI's UID.");
         }
         mSystemUiUid = systemUiUid;
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_BLUETOOTH,
-                (Runnable r) -> r.run(),
-                mDeviceConfigChangedListener);
     }
 
     /**
@@ -1383,10 +1380,12 @@
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
             mHandler.sendMessage(getMsg);
         }
+
+        mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
         if (mBluetoothAirplaneModeListener != null) {
-            mBluetoothAirplaneModeListener.start(
-                    new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext));
+            mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
         }
+        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
     }
 
     /**
@@ -2229,6 +2228,12 @@
                         Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
                     }
                     mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+                    if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                        mHandler.sendEmptyMessageDelayed(
+                                MESSAGE_INIT_FLAGS_CHANGED,
+                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+                        break;
+                    }
                     if (mBluetooth != null && isEnabled()) {
                         restartForReason(
                                 BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
new file mode 100644
index 0000000..242fa84
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020 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;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper class that handles callout and callback methods without
+ * complex logic.
+ */
+public class BluetoothModeChangeHelper {
+    private volatile BluetoothA2dp mA2dp;
+    private volatile BluetoothHearingAid mHearingAid;
+    private final BluetoothAdapter mAdapter;
+    private final Context mContext;
+
+    BluetoothModeChangeHelper(Context context) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mContext = context;
+
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener,
+                BluetoothProfile.HEARING_AID);
+    }
+
+    private final ServiceListener mProfileServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            // Setup Bluetooth profile proxies
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dp = (BluetoothA2dp) proxy;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAid = (BluetoothHearingAid) proxy;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            // Clear Bluetooth profile proxies
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dp = null;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAid = null;
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    @VisibleForTesting
+    public boolean isA2dpOrHearingAidConnected() {
+        return isA2dpConnected() || isHearingAidConnected();
+    }
+
+    @VisibleForTesting
+    public boolean isBluetoothOn() {
+        final BluetoothAdapter adapter = mAdapter;
+        if (adapter == null) {
+            return false;
+        }
+        return adapter.getLeState() == BluetoothAdapter.STATE_ON;
+    }
+
+    @VisibleForTesting
+    public boolean isAirplaneModeOn() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+    }
+
+    @VisibleForTesting
+    public void onAirplaneModeChanged(BluetoothManagerService managerService) {
+        managerService.onAirplaneModeChanged();
+    }
+
+    @VisibleForTesting
+    public int getSettingsInt(String name) {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                name, 0);
+    }
+
+    @VisibleForTesting
+    public void setSettingsInt(String name, int value) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                name, value);
+    }
+
+    @VisibleForTesting
+    public void showToastMessage() {
+        Resources r = mContext.getResources();
+        final CharSequence text = r.getString(
+                R.string.bluetooth_airplane_mode_toast, 0);
+        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+    }
+
+    private boolean isA2dpConnected() {
+        final BluetoothA2dp a2dp = mA2dp;
+        if (a2dp == null) {
+            return false;
+        }
+        return a2dp.getConnectedDevices().size() > 0;
+    }
+
+    private boolean isHearingAidConnected() {
+        final BluetoothHearingAid hearingAid = mHearingAid;
+        if (hearingAid == null) {
+            return false;
+        }
+        return hearingAid.getConnectedDevices().size() > 0;
+    }
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 95a7a22..20f68da 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -428,11 +428,18 @@
                 mPowerButtonConsecutiveTaps++;
                 mPowerButtonSlowConsecutiveTaps++;
             }
-            if (mPanicButtonGestureEnabled
-                    && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
-                launchPanic = true;
-                intercept = interactive;
-            } else if (mCameraDoubleTapPowerEnabled
+            // Check if we need to launch camera or panic flows
+            if (mPanicButtonGestureEnabled) {
+                // Commit to intercepting the powerkey event after the second "quick" tap to avoid
+                // lockscreen changes between launching camera and the panic flow.
+                if (mPowerButtonConsecutiveTaps > 1) {
+                    intercept = interactive;
+                }
+                if (mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+                    launchPanic = true;
+                }
+            }
+            if (mCameraDoubleTapPowerEnabled
                     && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
                     && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
                 launchCamera = true;
@@ -462,8 +469,11 @@
         mMetricsLogger.histogram("power_consecutive_short_tap_count",
                 mPowerButtonSlowConsecutiveTaps);
         mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
+
         outLaunched.value = launchCamera || launchPanic;
-        return intercept && (launchCamera || launchPanic);
+        // Intercept power key event if the press is part of a gesture (camera, panic) and the user
+        // has completed setup.
+        return intercept && isUserSetupComplete();
     }
 
     /**
@@ -473,8 +483,7 @@
     boolean handleCameraGesture(boolean useWakelock, int source) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture");
         try {
-            boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+            boolean userSetupComplete = isUserSetupComplete();
             if (!userSetupComplete) {
                 if (DBG) {
                     Slog.d(TAG, String.format(
@@ -512,8 +521,7 @@
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "GestureLauncher:handlePanicButtonGesture");
         try {
-            boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+            boolean userSetupComplete = isUserSetupComplete();
             if (!userSetupComplete) {
                 if (DBG) {
                     Slog.d(TAG, String.format(
@@ -536,6 +544,11 @@
         }
     }
 
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 49444834..ba1eda9 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -104,6 +104,7 @@
         "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
         "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec
+        "media.transcoding", // Media transcoding service
         "com.android.bluetooth",  // Bluetooth service
         "/apex/com.android.os.statsd/bin/statsd",  // Stats daemon
     };
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f0c9845..79a660a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14872,6 +14872,7 @@
             change |= UidRecord.CHANGE_IDLE;
         }
         final int enqueuedChange = mUidObserverController.enqueueUidChange(
+                uidRec == null ? null : uidRec.pendingChange,
                 uid, change, procState, procStateSeq, capability, ephemeral);
         if (uidRec != null) {
             uidRec.lastReportedChange = enqueuedChange;
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 529c651..f32423f 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -85,19 +85,19 @@
         sGlobalSettingToTypeMap.put(
                 Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE, String.class);
+                Settings.Global.ANGLE_DEBUG_PACKAGE, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, int.class);
+                Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, int.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, String.class);
+                Settings.Global.ANGLE_ALLOWLIST, String.class);
         sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_EGL_FEATURES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
+                Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 747e8a8..be63dd4 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -144,7 +144,8 @@
 
                     final PendingIntent contentIntent;
                     if (context.getPackageManager().resolveActivity(intent, 0) != null) {
-                        contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
+                        contentIntent = PendingIntent.getActivity(context, 0, intent,
+                                PendingIntent.FLAG_IMMUTABLE);
                     } else {
                         contentIntent = null;
                     }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2571205..53c6758 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1460,6 +1460,12 @@
             if (updateServiceConnectionActivities) {
                 mService.mServices.updateServiceConnectionActivitiesLocked(this);
             }
+            if (thread == null) {
+                // Only update lru and oom-adj if the process is alive. Because it may be called
+                // when cleaning up the last activity from handling process died, the dead process
+                // should not be added to lru list again.
+                return;
+            }
             mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
             if (updateOomAdj) {
                 mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index b3488c3..b3150d1 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -20,6 +20,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerProto;
 import android.app.IUidObserver;
@@ -29,7 +31,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
@@ -53,7 +54,7 @@
     final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
 
     @GuardedBy("mLock")
-    private final SparseArray<ChangeRecord> mPendingUidChanges = new SparseArray<>();
+    private final ArrayList<ChangeRecord> mPendingUidChanges = new ArrayList<>();
     @GuardedBy("mLock")
     private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
 
@@ -71,27 +72,27 @@
     private static final boolean VALIDATE_UID_STATES = true;
     private final ActiveUids mValidateUids;
 
-    UidObserverController(Handler handler) {
+    UidObserverController(@NonNull Handler handler) {
         mHandler = handler;
         mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
     }
 
-    void register(IUidObserver observer, int which, int cutpoint, String callingPackage,
-            int callingUid) {
+    void register(@NonNull IUidObserver observer, int which, int cutpoint,
+            @NonNull String callingPackage, int callingUid) {
         synchronized (mLock) {
             mUidObservers.register(observer, new UidObserverRegistration(callingUid,
                     callingPackage, which, cutpoint));
         }
     }
 
-    void unregister(IUidObserver observer) {
+    void unregister(@NonNull IUidObserver observer) {
         synchronized (mLock) {
             mUidObservers.unregister(observer);
         }
     }
 
-    int enqueueUidChange(int uid, int change, int procState, long procStateSeq,
-            int capability, boolean ephemeral) {
+    int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState,
+            long procStateSeq, int capability, boolean ephemeral) {
         synchronized (mLock) {
             if (mPendingUidChanges.size() == 0) {
                 if (DEBUG_UID_OBSERVERS) {
@@ -100,10 +101,11 @@
                 mHandler.post(mDispatchRunnable);
             }
 
-            ChangeRecord changeRecord = mPendingUidChanges.get(uid);
-            if (changeRecord == null) {
-                changeRecord = getOrCreateChangeRecordLocked();
-                mPendingUidChanges.put(uid, changeRecord);
+            final ChangeRecord changeRecord = currentRecord != null
+                    ? currentRecord : getOrCreateChangeRecordLocked();
+            if (!changeRecord.isPending) {
+                changeRecord.isPending = true;
+                mPendingUidChanges.add(changeRecord);
             } else {
                 change = mergeWithPendingChange(change, changeRecord.change);
             }
@@ -119,7 +121,7 @@
         }
     }
 
-    SparseArray<ChangeRecord> getPendingUidChangesForTest() {
+    ArrayList<ChangeRecord> getPendingUidChangesForTest() {
         return mPendingUidChanges;
     }
 
@@ -175,7 +177,10 @@
                 mActiveUidChanges = new ChangeRecord[numUidChanges];
             }
             for (int i = 0; i < numUidChanges; i++) {
-                mActiveUidChanges[i] = mPendingUidChanges.valueAt(i);
+                final ChangeRecord changeRecord = mPendingUidChanges.get(i);
+                mActiveUidChanges[i] = getOrCreateChangeRecordLocked();
+                changeRecord.copyTo(mActiveUidChanges[i]);
+                changeRecord.isPending = false;
             }
             mPendingUidChanges.clear();
             if (DEBUG_UID_OBSERVERS) {
@@ -216,13 +221,15 @@
 
         synchronized (mLock) {
             for (int j = 0; j < numUidChanges; j++) {
-                mAvailUidChanges.add(mActiveUidChanges[j]);
+                final ChangeRecord changeRecord = mActiveUidChanges[j];
+                changeRecord.isPending = false;
+                mAvailUidChanges.add(changeRecord);
             }
         }
     }
 
-    private void dispatchUidsChangedForObserver(IUidObserver observer,
-            UidObserverRegistration reg, int changesSize) {
+    private void dispatchUidsChangedForObserver(@NonNull IUidObserver observer,
+            @NonNull UidObserverRegistration reg, int changesSize) {
         if (observer == null) {
             return;
         }
@@ -318,7 +325,7 @@
         return mValidateUids.get(uid);
     }
 
-    void dump(PrintWriter pw, String dumpPackage) {
+    void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) {
         synchronized (mLock) {
             final int count = mUidObservers.getRegisteredCallbackCount();
             boolean printed = false;
@@ -353,7 +360,7 @@
         }
     }
 
-    void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
+    void dumpDebug(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage) {
         synchronized (mLock) {
             final int count = mUidObservers.getRegisteredCallbackCount();
             for (int i = 0; i < count; i++) {
@@ -366,23 +373,34 @@
         }
     }
 
-    boolean dumpValidateUids(PrintWriter pw, String dumpPackage, int dumpAppId,
-            String header, boolean needSep) {
+    boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId,
+            @NonNull String header, boolean needSep) {
         return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
     }
 
-    void dumpValidateUidsProto(ProtoOutputStream proto, String dumpPackage,
+    void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage,
             int dumpAppId, long fieldId) {
         mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
     }
 
     static final class ChangeRecord {
+        public boolean isPending;
         public int uid;
         public int change;
         public int procState;
         public int capability;
         public boolean ephemeral;
         public long procStateSeq;
+
+        void copyTo(@NonNull ChangeRecord changeRecord) {
+            changeRecord.isPending = isPending;
+            changeRecord.uid = uid;
+            changeRecord.change = change;
+            changeRecord.procState = procState;
+            changeRecord.capability = capability;
+            changeRecord.ephemeral = ephemeral;
+            changeRecord.procStateSeq = procStateSeq;
+        }
     }
 
     private static final class UidObserverRegistration {
@@ -416,7 +434,7 @@
                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
         };
 
-        UidObserverRegistration(int uid, String pkg, int which, int cutpoint) {
+        UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
             this.mUid = uid;
             this.mPkg = pkg;
             this.mWhich = which;
@@ -425,7 +443,7 @@
                     ? new SparseIntArray() : null;
         }
 
-        void dump(PrintWriter pw, IUidObserver observer) {
+        void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) {
             pw.print("    ");
             UserHandle.formatUid(pw, mUid);
             pw.print(" ");
@@ -460,7 +478,7 @@
             }
         }
 
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
             proto.write(UidObserverRegistrationProto.UID, mUid);
             proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index dfd6149..f1945ed 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -27,6 +27,7 @@
 import android.util.proto.ProtoUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.am.UidObserverController.ChangeRecord;
 
 /**
  * Overall information about a uid that has actively running processes.
@@ -107,6 +108,9 @@
             UidRecordProto.CHANGE_UNCACHED,
     };
 
+    // UidObserverController is the only thing that should modify this.
+    final ChangeRecord pendingChange = new ChangeRecord();
+
     int lastReportedChange;
 
     public UidRecord(int _uid) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f1bad98..d1126ee 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1307,7 +1307,7 @@
                 Slog.w(TAG, "No user info for user #" + userId);
                 return false;
             }
-            if (foreground && userInfo.isManagedProfile()) {
+            if (foreground && userInfo.isProfile()) {
                 Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                 return false;
             }
@@ -1612,7 +1612,7 @@
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
             return false;
         }
-        if (targetUserInfo.isManagedProfile()) {
+        if (targetUserInfo.isProfile()) {
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
             return false;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 5c08bce..e738d17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -57,7 +57,7 @@
     }
 
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
-    private final BiometricUtils mBiometricUtils;
+    private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final List<S> mEnrolledList;
     private ClientMonitor<T> mCurrentTask;
@@ -95,15 +95,15 @@
 
     protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
             LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
-            List<S> enrolledList, BiometricUtils utils, int sensorId);
+            List<S> enrolledList, BiometricUtils<S> utils, int sensorId);
 
-    protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
-            IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
-            int sensorId, Map<Integer, Long> authenticatorIds);
+    protected abstract RemovalClient<S, T> getRemovalClient(Context context,
+            LazyDaemon<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
+            BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds);
 
     protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             int userId, @NonNull String owner, int sensorId, int statsModality,
-            @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+            @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
                 userId, owner, 0 /* cookie */, sensorId, statsModality,
@@ -153,7 +153,7 @@
                     + mCurrentTask.getClass().getSimpleName());
             return;
         }
-        ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining);
+        ((RemovalClient<S, T>) mCurrentTask).onRemoved(identifier, remaining);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 1348f79..f79abd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
@@ -29,17 +30,18 @@
 /**
  * A class to keep track of the remove state for a given client.
  */
-public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer {
+public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T>
+        extends ClientMonitor<T> implements RemovalConsumer {
 
     private static final String TAG = "Biometrics/RemovalClient";
 
     protected final int mBiometricId;
-    private final BiometricUtils mBiometricUtils;
+    private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
 
     public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils,
             int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 statsModality, BiometricsProtoEnums.ACTION_REMOVE,
@@ -63,8 +65,8 @@
     }
 
     @Override
-    public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
-        if (identifier.getBiometricId() != 0) {
+    public void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (identifier != null) {
             mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
                     identifier.getBiometricId());
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
index 3d7988c..0aba7e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.Nullable;
 import android.hardware.biometrics.BiometricAuthenticator;
 
 /**
@@ -28,5 +29,5 @@
      * @param remaining number of templates that still need to be removed before the operation in
      *                  the HAL is complete (e.g. when removing all templates).
      */
-    void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining);
+    void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index c2d4c15..f7998ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -219,8 +219,7 @@
                         removalConsumer.onRemoved(face, remaining);
                     }
                 } else {
-                    final Face face = new Face("", 0 /* identifier */, deviceId);
-                    removalConsumer.onRemoved(face, 0 /* remaining */);
+                    removalConsumer.onRemoved(null, 0 /* remaining */);
                 }
 
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index 3e843ca..989b5c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -23,6 +23,7 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.NativeHandle;
@@ -53,9 +54,9 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
-            @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
-            int sensorId) {
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
+            @Nullable NativeHandle surfaceHandle, int sensorId) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 93f35f4..7626587 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -40,7 +40,7 @@
 
     FaceInternalCleanupClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
-            int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+            int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
                 enrolledList, utils, authenticatorIds);
@@ -49,15 +49,15 @@
     @Override
     protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
             LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
-            List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+            List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
         return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
                 enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+    protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
             LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+            int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
             Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index 32d4832..4166dff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -40,8 +40,8 @@
 
     FaceInternalEnumerateClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
-            @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
-            int sensorId) {
+            @NonNull String owner, @NonNull List<Face> enrolledList,
+            @NonNull BiometricUtils<Face> utils, int sensorId) {
         super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FACE);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index dde5aba..31ae3a3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -34,12 +35,12 @@
  * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
+class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
     private static final String TAG = "FaceRemovalClient";
 
     FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
             int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
                 authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index dc22970..1f71d78 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -174,7 +174,7 @@
         }
 
         @Override // Binder call
-        public void generateChallenge(IBinder token, int sensorId,
+        public void generateChallenge(IBinder token, int sensorId, int userId,
                 IFingerprintServiceReceiver receiver, String opPackageName) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
@@ -184,20 +184,21 @@
                 return;
             }
 
-            provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName);
+            provider.scheduleGenerateChallenge(sensorId, userId, token, receiver, opPackageName);
         }
 
         @Override // Binder call
-        public void revokeChallenge(IBinder token, String opPackageName, long challenge) {
+        public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
+                long challenge) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
-            final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+            final ServiceProvider provider = getProviderForSensor(sensorId);
             if (provider == null) {
-                Slog.w(TAG, "Null provider for revokeChallenge");
+                Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
                 return;
             }
 
-            provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName,
+            provider.scheduleRevokeChallenge(sensorId, userId, token, opPackageName,
                     challenge);
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 35d0188..c2315fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -64,10 +64,10 @@
 
     void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
 
-    void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+    void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, String opPackageName);
 
-    void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+    void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge);
 
     void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index e923943..8acf4f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -43,7 +43,7 @@
  * Fingerprint-specific authentication client supporting the
  * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
  */
-public class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements
+class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements
         Udfps, LockoutConsumer {
     private static final String TAG = "FingerprintAuthenticationClient";
 
@@ -51,7 +51,7 @@
     @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
     @Nullable private ICancellationSignal mCancellationSignal;
 
-    public FingerprintAuthenticationClient(@NonNull Context context,
+    FingerprintAuthenticationClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
new file mode 100644
index 0000000..3398323
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+/**
+ * Performs fingerprint detection without exposing any matching information (e.g. accept/reject
+ * have the same haptic, lockout counter is not increased).
+ */
+class FingerprintDetectClient extends AcquisitionClient<ISession> {
+
+    private static final String TAG = "FingerprintDetectClient";
+
+    private final boolean mIsStrongBiometric;
+    @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+
+    @Nullable private ICancellationSignal mCancellationSignal;
+
+    FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int sensorId,
+            @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
+            int statsClient) {
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
+                statsClient);
+        mIsStrongBiometric = isStrongBiometric;
+        mUdfpsOverlayController = udfpsOverlayController;
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    protected void startHalOperation() {
+        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting finger detect", e);
+            UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    void onInteractionDetected() {
+        vibrateSuccess();
+
+        try {
+            getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+            mCallback.onClientFinished(this, true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when sending onDetected", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 7a7ad64..437ecd7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -24,8 +24,8 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.hardware.keymaster.HardwareAuthToken;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -38,7 +38,7 @@
 import com.android.server.biometrics.sensors.fingerprint.Udfps;
 import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
-public class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
+class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
 
     private static final String TAG = "FingerprintEnrollClient";
 
@@ -46,11 +46,11 @@
     @Nullable private ICancellationSignal mCancellationSignal;
     private final int mMaxTemplatesPerUser;
 
-    public FingerprintEnrollClient(@NonNull Context context,
+    FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
-            @NonNull FingerprintUtils utils, int sensorId,
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 7db01ee..402886b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.IGenerateChallengeCallback;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -30,28 +30,12 @@
 /**
  * Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
  */
-public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<IFingerprint> {
+class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> {
     private static final String TAG = "FingerprintGenerateChallengeClient";
     private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
-    private final IGenerateChallengeCallback mGenerateChallengeCallback =
-            new IGenerateChallengeCallback.Stub() {
-        @Override
-        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
-            try {
-                getListener().onChallengeGenerated(sensorId, challenge);
-                mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
-                        true /* success */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to send challenge", e);
-                mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
-                        false /* success */);
-            }
-        }
-    };
-
-    public FingerprintGenerateChallengeClient(@NonNull Context context,
-            @NonNull LazyDaemon<IFingerprint> lazyDaemon,
+    FingerprintGenerateChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon,
             @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener,
             @NonNull String owner, int sensorId) {
@@ -61,11 +45,21 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().generateChallenge(getSensorId(), getTargetUserId(),
-                    CHALLENGE_TIMEOUT_SEC,
-                    mGenerateChallengeCallback);
+            getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to generateChallenge", e);
         }
     }
+
+    void onChallengeGenerated(int sensorId, int userId, long challenge) {
+        try {
+            getListener().onChallengeGenerated(sensorId, challenge);
+            mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                    true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send challenge", e);
+            mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                    false /* success */);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
new file mode 100644
index 0000000..fec3cff
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.Map;
+
+class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> {
+
+    private static final String TAG = "FingerprintGetAuthenticatorIdClient";
+
+    private final Map<Integer, Long> mAuthenticatorIds;
+
+    FingerprintGetAuthenticatorIdClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mAuthenticatorIds = authenticatorIds;
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().getAuthenticatorId(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
+    void onAuthenticatorIdRetrieved(long authenticatorId) {
+        mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
+        mCallback.onClientFinished(this, true /* success */);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
new file mode 100644
index 0000000..2a0e984
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalCleanupClient;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+import com.android.server.biometrics.sensors.RemovalClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fingerprint-specific internal cleanup client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, ISession> {
+
+    FingerprintInternalCleanupClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, @NonNull List<Fingerprint> enrolledList,
+            @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, userId, owner, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+    }
+
+    @Override
+    protected InternalEnumerateClient<ISession> getEnumerateClient(Context context,
+            LazyDaemon<ISession> lazyDaemon, IBinder token, int userId, String owner,
+            List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) {
+        return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+                enrolledList, utils, sensorId);
+    }
+
+    @Override
+    protected RemovalClient<Fingerprint, ISession> getRemovalClient(Context context,
+            LazyDaemon<ISession> lazyDaemon, IBinder token, int biometricId, int userId,
+            String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+            Map<Integer, Long> authenticatorIds) {
+        return new FingerprintRemovalClient(context, lazyDaemon, token,
+                null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+                sensorId, authenticatorIds);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
new file mode 100644
index 0000000..c930360
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+
+import java.util.List;
+
+/**
+ * Fingerprint-specific internal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalEnumerateClient extends InternalEnumerateClient<ISession> {
+    private static final String TAG = "FingerprintInternalEnumerateClient";
+
+    protected FingerprintInternalEnumerateClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, int userId,
+            @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+        super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().enumerateEnrollments(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting enumerate", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f16aa8a..d713f98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -23,7 +23,7 @@
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.fingerprint.Fingerprint;
@@ -35,6 +35,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserManager;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Surface;
@@ -44,6 +45,7 @@
 import com.android.server.biometrics.sensors.ClientMonitor;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
@@ -69,6 +71,7 @@
     @NonNull private final IActivityTaskManager mActivityTaskManager;
     @NonNull private final BiometricTaskStackListener mTaskStackListener;
 
+    @Nullable private IFingerprint mDaemon;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
 
     private final class BiometricTaskStackListener extends TaskStackListener {
@@ -143,15 +146,21 @@
 
     @Nullable
     private synchronized IFingerprint getHalInstance() {
-        final IFingerprint daemon = IFingerprint.Stub.asInterface(
+        if (mDaemon != null) {
+            return mDaemon;
+        }
+
+        Slog.d(getTag(), "Daemon was null, reconnecting");
+
+        mDaemon = IFingerprint.Stub.asInterface(
                 ServiceManager.waitForDeclaredService(mHalInstanceName));
-        if (daemon == null) {
+        if (mDaemon == null) {
             Slog.e(getTag(), "Unable to get daemon");
             return null;
         }
 
         try {
-            daemon.asBinder().linkToDeath(this, 0 /* flags */);
+            mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
         } catch (RemoteException e) {
             Slog.e(getTag(), "Unable to linkToDeath", e);
         }
@@ -162,7 +171,7 @@
             scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
         }
 
-        return daemon;
+        return mDaemon;
     }
 
     private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) {
@@ -191,14 +200,6 @@
         mSensors.get(sensorId).createNewSession(daemon, sensorId, userId);
     }
 
-    private void scheduleLoadAuthenticatorIdsWithoutHandler(int sensorId) {
-
-    }
-
-    private void scheduleLoadAuthenticatorIds(int sensorId) {
-
-    }
-
     @Override
     public boolean containsSensor(int sensorId) {
         return mSensors.contains(sensorId);
@@ -214,6 +215,39 @@
         return props;
     }
 
+    private void scheduleLoadAuthenticatorIds(int sensorId) {
+        for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+            scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
+        }
+    }
+
+    private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during loadAuthenticatorIds, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintGetAuthenticatorIdClient client =
+                        new FingerprintGetAuthenticatorIdClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), userId,
+                                mContext.getOpPackageName(), sensorId,
+                                mSensors.get(sensorId).getAuthenticatorIds());
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling loadAuthenticatorId"
+                        + ", sensorId: " + sensorId
+                        + ", userId: " + userId, e);
+            }
+        });
+    }
+
     @Override
     public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
         mHandler.post(() -> {
@@ -240,24 +274,55 @@
     }
 
     @Override
-    public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+    public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, String opPackageName) {
         mHandler.post(() -> {
-            final FingerprintGenerateChallengeClient client =
-                    new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
-                            new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
-            scheduleForSensor(sensorId, client);
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during generateChallenge, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintGenerateChallengeClient client =
+                        new FingerprintGenerateChallengeClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), token,
+                                new ClientMonitorCallbackConverter(receiver), opPackageName,
+                                sensorId);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling generateChallenge", e);
+            }
         });
     }
 
     @Override
-    public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+    public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge) {
         mHandler.post(() -> {
-            final FingerprintRevokeChallengeClient client =
-                    new FingerprintRevokeChallengeClient(mContext, mLazyDaemon, token,
-                            opPackageName, sensorId, challenge);
-            scheduleForSensor(sensorId, client);
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during revokeChallenge, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintRevokeChallengeClient client =
+                        new FingerprintRevokeChallengeClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), token,
+                                opPackageName, sensorId, challenge);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling revokeChallenge", e);
+            }
         });
     }
 
@@ -292,7 +357,7 @@
                     public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
                             boolean success) {
                         if (success) {
-                            scheduleLoadAuthenticatorIdsWithoutHandler(sensorId);
+                            scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
                         }
                     }
                 });
@@ -311,7 +376,31 @@
     public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
             @Nullable Surface surface, int statsClient) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during finger detect, sensorId: " + sensorId);
+                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+                // this operation. We should not send the callback yet, since the scheduler may
+                // be processing something else.
+                return;
+            }
 
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+                final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+                        mSensors.get(sensorId).getLazySession(), token, callback, userId,
+                        opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
+                        statsClient);
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling finger detect", e);
+            }
+        });
     }
 
     @Override
@@ -390,24 +479,48 @@
     }
 
     @Override
-    public void scheduleInternalCleanup(int userId, int sensorId) {
+    public void scheduleInternalCleanup(int sensorId, int userId) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during internal cleanup, sensorId: " + sensorId);
+                return;
+            }
 
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
+                final FingerprintInternalCleanupClient client =
+                        new FingerprintInternalCleanupClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), userId,
+                                mContext.getOpPackageName(), sensorId, enrolledList,
+                                FingerprintUtils.getInstance(sensorId),
+                                mSensors.get(sensorId).getAuthenticatorIds());
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
+            }
+        });
     }
 
     @Override
     public boolean isHardwareDetected(int sensorId) {
-        return false;
+        return getHalInstance() != null;
     }
 
     @Override
     public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
-
+        FingerprintUtils.getInstance(sensorId)
+                .renameBiometricForUser(mContext, userId, fingerId, name);
     }
 
     @NonNull
     @Override
     public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
-        return new ArrayList<>();
+        return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
     }
 
     @Override
@@ -417,7 +530,7 @@
 
     @Override
     public long getAuthenticatorId(int sensorId, int userId) {
-        return 0;
+        return mSensors.get(sensorId).getAuthenticatorIds().getOrDefault(userId, 0L);
     }
 
     @Override
@@ -459,6 +572,14 @@
 
     @Override
     public void binderDied() {
+        Slog.e(getTag(), "HAL died");
+        mHandler.post(() -> {
+            mDaemon = null;
 
+            for (int i = 0; i < mSensors.size(); i++) {
+                final int sensorId = mSensors.keyAt(i);
+                PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+            }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index df063dc..4a99a7b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -17,16 +17,18 @@
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.RemovalClient;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
 import java.util.Map;
 
@@ -34,13 +36,13 @@
  * Fingerprint-specific removal client supporting the
  * {@link android.hardware.biometrics.fingerprint.IFingerprint} interface.
  */
-public class FingerprintRemovalClient extends RemovalClient<ISession> {
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, ISession> {
     private static final String TAG = "FingerprintRemovalClient";
 
-    public FingerprintRemovalClient(@NonNull Context context,
+    FingerprintRemovalClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull FingerprintUtils utils, int sensorId,
+            @Nullable ClientMonitorCallbackConverter listener, int biometricId, int userId,
+            @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
                 authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index bc35ad4..1718126 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -35,7 +35,7 @@
  * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
  * cleared.
  */
-public class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
+class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
 
     private static final String TAG = "FingerprintResetLockoutClient";
 
@@ -43,7 +43,7 @@
     private final LockoutCache mLockoutCache;
     private final LockoutResetDispatcher mLockoutResetDispatcher;
 
-    public FingerprintResetLockoutClient(@NonNull Context context,
+    FingerprintResetLockoutClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId,
             @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index e97dbe7..ebb4fe6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.IRevokeChallengeCallback;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -29,25 +29,14 @@
 /**
  * Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
  */
-public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<IFingerprint> {
+class FingerprintRevokeChallengeClient extends RevokeChallengeClient<ISession> {
 
     private static final String TAG = "FingerpirntRevokeChallengeClient";
 
     private final long mChallenge;
 
-    private final IRevokeChallengeCallback mRevokeChallengeCallback =
-            new IRevokeChallengeCallback.Stub() {
-        @Override
-        public void onChallengeRevoked(int sensorId, int userId, long challenge) {
-            final boolean success = challenge == mChallenge;
-            mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
-        }
-    };
-
-    public FingerprintRevokeChallengeClient(
-            @NonNull Context context,
-            @NonNull LazyDaemon<IFingerprint> lazyDaemon,
-            @NonNull IBinder token,
+    FingerprintRevokeChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
             @NonNull String owner, int sensorId, long challenge) {
         super(context, lazyDaemon, token, owner, sensorId);
         mChallenge = challenge;
@@ -56,10 +45,14 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().revokeChallenge(getSensorId(), getTargetUserId(), mChallenge,
-                    mRevokeChallengeCallback);
+            getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to revokeChallenge", e);
         }
     }
+
+    void onChallengeRevoked(int sensorId, int userId, long challenge) {
+        final boolean success = challenge == mChallenge;
+        mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
index 2abbcb0..2fae1f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
@@ -23,7 +23,7 @@
 /**
  * For a single sensor, caches lockout states for all users.
  */
-public class LockoutCache implements LockoutTracker {
+class LockoutCache implements LockoutTracker {
 
     // Map of userId to LockoutMode
     private final SparseIntArray mUserLockoutStates;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 5f5f44ba..d4ce896 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -19,25 +19,31 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.Error;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.ISessionCallback;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.Interruptable;
 import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.RemovalConsumer;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
@@ -51,7 +57,8 @@
  * Maintains the state of a single sensor within an instance of the
  * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
  */
-class Sensor {
+@SuppressWarnings("deprecation")
+class Sensor implements IBinder.DeathRecipient {
     @NonNull private final String mTag;
     @NonNull private final Context mContext;
     @NonNull private final Handler mHandler;
@@ -60,9 +67,30 @@
     @NonNull private final LockoutCache mLockoutCache;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
 
-    @Nullable private Session mCurrentSession; // TODO: Death recipient
+    @Nullable private Session mCurrentSession;
     @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
 
+    @Override
+    public void binderDied() {
+        Slog.e(mTag, "Binder died");
+        mHandler.post(() -> {
+            final ClientMonitor<?> client = mScheduler.getCurrentClient();
+            if (client instanceof Interruptable) {
+                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+                final Interruptable interruptable = (Interruptable) client;
+                interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+
+                mScheduler.recordCrashState();
+
+                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
+                mCurrentSession = null;
+            }
+        });
+    }
+
     private static class Session {
         @NonNull private final String mTag;
         @NonNull private final ISession mSession;
@@ -100,6 +128,7 @@
         return mSensorProperties;
     }
 
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
     boolean hasSessionForUser(int userId) {
         return mCurrentSession != null && mCurrentSession.mUserId == userId;
     }
@@ -109,7 +138,39 @@
         final ISessionCallback callback = new ISessionCallback.Stub() {
             @Override
             public void onStateChanged(int cookie, byte state) {
+                // TODO(b/162973174)
+            }
 
+            @Override
+            public void onChallengeGenerated(long challenge) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintGenerateChallengeClient)) {
+                        Slog.e(mTag, "onChallengeGenerated for wrong client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintGenerateChallengeClient generateChallengeClient =
+                            (FingerprintGenerateChallengeClient) client;
+                    generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge);
+                });
+            }
+
+            @Override
+            public void onChallengeRevoked(long challenge) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintRevokeChallengeClient)) {
+                        Slog.e(mTag, "onChallengeRevoked for wrong client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintRevokeChallengeClient revokeChallengeClient =
+                            (FingerprintRevokeChallengeClient) client;
+                    revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge);
+                });
             }
 
             @Override
@@ -260,31 +321,89 @@
 
             @Override
             public void onInteractionDetected() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintDetectClient)) {
+                        Slog.e(mTag, "onInteractionDetected for non-detect client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
 
+                    final FingerprintDetectClient fingerprintDetectClient =
+                            (FingerprintDetectClient) client;
+                    fingerprintDetectClient.onInteractionDetected();
+                });
             }
 
             @Override
             public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof EnumerateConsumer)) {
+                        Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
 
+                    final EnumerateConsumer enumerateConsumer =
+                            (EnumerateConsumer) client;
+                    if (enrollmentIds.length > 0) {
+                        for (int i = 0; i < enrollmentIds.length; i++) {
+                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+                            enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
+                        }
+                    } else {
+                        enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+                    }
+                });
             }
 
             @Override
             public void onEnrollmentsRemoved(int[] enrollmentIds) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof RemovalConsumer)) {
+                        Slog.e(mTag, "onRemoved for non-removal consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
 
+                    final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+                    if (enrollmentIds.length > 0) {
+                        for (int i  = 0; i < enrollmentIds.length; i++) {
+                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+                            removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
+                        }
+                    } else {
+                        removalConsumer.onRemoved(null, 0);
+                    }
+                });
             }
 
             @Override
             public void onAuthenticatorIdRetrieved(long authenticatorId) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
+                        Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
 
+                    final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
+                            (FingerprintGetAuthenticatorIdClient) client;
+                    getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
+                });
             }
 
             @Override
             public void onAuthenticatorIdInvalidated() {
-
+                // TODO(159667191)
             }
         };
 
         final ISession newSession = daemon.createSession(sensorId, userId, callback);
+        newSession.asBinder().linkToDeath(this, 0 /* flags */);
         mCurrentSession = new Session(mTag, newSession, userId, callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index da68bc8..ab4427c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -505,7 +505,7 @@
     }
 
     @Override
-    public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+    public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
         mHandler.post(() -> {
             final FingerprintGenerateChallengeClient client =
@@ -517,7 +517,7 @@
     }
 
     @Override
-    public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+    public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge) {
         mHandler.post(() -> {
             final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
@@ -632,7 +632,7 @@
     }
 
     @Override
-    public void scheduleInternalCleanup(int userId, int sensorId) {
+    public void scheduleInternalCleanup(int sensorId, int userId) {
         scheduleInternalCleanup(userId);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 975ac3d..c750b90 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -48,8 +49,8 @@
     FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
-            int timeoutSec, int sensorId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index e061112..a42a8ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -42,7 +42,8 @@
     FingerprintInternalCleanupClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
             @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
-            @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+            @NonNull BiometricUtils<Fingerprint> utils,
+            @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, userId, owner, sensorId,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
     }
@@ -50,18 +51,17 @@
     @Override
     protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
             Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
-            int userId, String owner,
-            List<Fingerprint> enrolledList, BiometricUtils utils,
-            int sensorId) {
+            int userId, String owner, List<Fingerprint> enrolledList,
+            BiometricUtils<Fingerprint> utils, int sensorId) {
         return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
                 enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+    protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
             LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
-            Map<Integer, Long> authenticatorIds) {
+            int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
+            int sensorId, Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
         return new FingerprintRemovalClient(context, lazyDaemon, token,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index 5fd1d1e..7117cf3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -41,7 +41,7 @@
     FingerprintInternalEnumerateClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
-            @NonNull BiometricUtils utils, int sensorId) {
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
         super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 4bbb7ef..f6a22f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -35,13 +36,13 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> {
     private static final String TAG = "FingerprintRemovalClient";
 
     FingerprintRemovalClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+            @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
                 authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 4fc1545..dc5dace9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -135,6 +135,6 @@
     private PendingIntent getLockoutResetIntentForUser(int userId) {
         return PendingIntent.getBroadcast(mContext, userId,
                 new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 2e9818d..bc3bff1 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -63,7 +63,7 @@
     private Map<String, Boolean> mPackageOverrides;
 
     public CompatChange(long changeId) {
-        this(changeId, null, -1, false, false, null);
+        this(changeId, null, -1, -1, false, false, null);
     }
 
     /**
@@ -71,11 +71,14 @@
      * @param name Short descriptive name.
      * @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter};
      *                             -1 if the change is always enabled.
+     * @param enableSinceTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledSince};
+     *                             -1 if the change is always enabled.
      * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
      */
     public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
-            boolean disabled, boolean loggingOnly, String description) {
-        super(changeId, name, enableAfterTargetSdk, disabled, loggingOnly, description);
+            int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) {
+        super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
+              description);
     }
 
     /**
@@ -83,7 +86,8 @@
      */
     public CompatChange(Change change) {
         super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
-                change.getDisabled(), change.getLoggingOnly(), change.getDescription());
+                change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
+                change.getDescription());
     }
 
     void registerListener(ChangeListener listener) {
@@ -145,8 +149,8 @@
         if (getDisabled()) {
             return false;
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            return app.targetSdkVersion > getEnableAfterTargetSdk();
+        if (getEnableSinceTargetSdk() != -1) {
+            return app.targetSdkVersion >= getEnableSinceTargetSdk();
         }
         return true;
     }
@@ -167,8 +171,8 @@
         if (getName() != null) {
             sb.append("; name=").append(getName());
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+        if (getEnableSinceTargetSdk() != -1) {
+            sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk());
         }
         if (getDisabled()) {
             sb.append("; disabled");
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index aeaa1fe..d80c58b 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -192,16 +192,19 @@
     }
 
     /**
-     * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not
+     * Returns the maximum sdk version for which this change can be opted in (or -1 if it is not
      * target sdk gated).
      */
-    int minTargetSdkForChangeId(long changeId) {
+    int maxTargetSdkForChangeIdOptIn(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
             if (c == null) {
-                return 0;
+                return -1;
             }
-            return c.getEnableAfterTargetSdk();
+            if (c.getEnableSinceTargetSdk() != -1) {
+                return c.getEnableSinceTargetSdk() - 1;
+            }
+            return -1;
         }
     }
 
@@ -318,7 +321,7 @@
         }
     }
 
-    private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName,
+    private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
                                                              int targetSdkVersion)
                     throws RemoteException {
         LongArray allowed = new LongArray();
@@ -326,7 +329,7 @@
             for (int i = 0; i < mChanges.size(); ++i) {
                 try {
                     CompatChange change = mChanges.valueAt(i);
-                    if (change.getEnableAfterTargetSdk() != targetSdkVersion) {
+                    if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
                         continue;
                     }
                     OverrideAllowedState allowedState =
@@ -345,14 +348,14 @@
     }
 
     /**
-     * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
      * @return The number of changes that were toggled.
      */
     int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
             throws RemoteException {
-        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, true);
         }
@@ -361,14 +364,14 @@
 
 
     /**
-     * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
      * @return The number of changes that were toggled.
      */
     int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
             throws RemoteException {
-        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, false);
         }
@@ -448,12 +451,7 @@
             CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange change = mChanges.valueAt(i);
-                changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
-                        change.getName(),
-                        change.getEnableAfterTargetSdk(),
-                        change.getDisabled(),
-                        change.getLoggingOnly(),
-                        change.getDescription());
+                changeInfos[i] = new CompatibilityChangeInfo(change);
             }
             return changeInfos;
         }
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index 08d2664..79a13ca 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -58,7 +58,7 @@
 
         boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
         boolean finalBuild = mAndroidBuildClassifier.isFinalBuild();
-        int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
+        int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId);
         boolean disabled = mCompatConfig.isDisabled(changeId);
 
         // Allow any override for userdebug or eng builds.
@@ -82,16 +82,16 @@
         }
         // Allow overriding any change for debuggable apps on non-final builds.
         if (!finalBuild) {
-            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
         }
         // Do not allow overriding default enabled changes on user builds
-        if (minTargetSdk == -1 && !disabled) {
-            return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
+        if (maxTargetSdk == -1 && !disabled) {
+            return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, maxTargetSdk);
         }
         // Only allow to opt-in for a targetSdk gated change.
-        if (disabled || appTargetSdk <= minTargetSdk) {
-            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+        if (disabled || appTargetSdk <= maxTargetSdk) {
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
         }
-        return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
+        return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk);
     }
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index c7de8d3..e4f52f1 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -59,8 +59,8 @@
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
 
-    private static int sMinTargetSdk = Build.VERSION_CODES.P;
-    private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
+    private static int sMinTargetSdk = Build.VERSION_CODES.Q;
+    private static int sMaxTargetSdk = Build.VERSION_CODES.R;
 
     public PlatformCompat(Context context) {
         mContext = context;
@@ -381,9 +381,9 @@
         if (change.getLoggingOnly()) {
             return false;
         }
-        if (change.getEnableAfterTargetSdk() > 0) {
-            if (change.getEnableAfterTargetSdk() < sMinTargetSdk
-                    || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
+        if (change.getEnableSinceTargetSdk() > 0) {
+            if (change.getEnableSinceTargetSdk() < sMinTargetSdk
+                    || change.getEnableSinceTargetSdk() > sMaxTargetSdk) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 858cce4..6597aa5 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -69,21 +69,27 @@
 
     private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
 
-    @SuppressWarnings("unused")  // Becomes active at instantiation time.
-    private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver;
+    private final Injector mInjector;
 
 
     // Called with SyncRoot lock held.
     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
+        this(syncRoot, context, handler, listener, new Injector());
+    }
+
+    LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+            Context context, Handler handler, Listener listener, Injector injector) {
         super(syncRoot, context, handler, listener, TAG);
+        mInjector = injector;
     }
 
     @Override
     public void registerLocked() {
         super.registerLocked();
 
-        mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper());
+        mInjector.setDisplayEventListenerLocked(getHandler().getLooper(),
+                new LocalDisplayEventListener());
 
         for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
             tryConnectDisplayLocked(physicalDisplayId);
@@ -183,13 +189,11 @@
         private int mDefaultModeId;
         private int mDefaultConfigGroup;
         private int mActiveModeId;
-        private boolean mActiveModeInvalid;
         private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
                 new DisplayModeDirector.DesiredDisplayModeSpecs();
         private boolean mDisplayModeSpecsInvalid;
         private int mActiveConfigId;
         private int mActiveColorMode;
-        private boolean mActiveColorModeInvalid;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
         private boolean mGameContentTypeSupported;
@@ -300,7 +304,6 @@
             // Schedule traversals to ensure that the correct state is reapplied if necessary.
             if (mActiveModeId != NO_DISPLAY_MODE_ID
                     && mActiveModeId != activeRecord.mMode.getModeId()) {
-                mActiveModeInvalid = true;
                 sendTraversalRequestLocked();
             }
 
@@ -373,7 +376,6 @@
                             + " mode.");
                 }
                 mActiveModeId = mDefaultModeId;
-                mActiveModeInvalid = true;
             }
 
             // Schedule traversals so that we apply pending changes.
@@ -464,14 +466,12 @@
                     Slog.w(TAG, "Active color mode no longer available, reverting"
                             + " to default mode.");
                     mActiveColorMode = Display.COLOR_MODE_DEFAULT;
-                    mActiveColorModeInvalid = true;
                 } else {
                     if (!mSupportedColorModes.isEmpty()) {
                         // This should never happen.
                         Slog.e(TAG, "Default and active color mode is no longer available!"
                                 + " Reverting to first available mode.");
                         mActiveColorMode = mSupportedColorModes.get(0);
-                        mActiveColorModeInvalid = true;
                     } else {
                         // This should really never happen.
                         Slog.e(TAG, "No color modes available!");
@@ -848,8 +848,7 @@
             }
             mActiveConfigId = activeConfigId;
             mActiveModeId = findMatchingModeIdLocked(activeConfigId);
-            mActiveModeInvalid = mActiveModeId == NO_DISPLAY_MODE_ID;
-            if (mActiveModeInvalid) {
+            if (mActiveModeId == NO_DISPLAY_MODE_ID) {
                 Slog.w(TAG, "In unknown mode after setting allowed configs"
                         + ", activeConfigId=" + mActiveConfigId);
             }
@@ -867,7 +866,6 @@
             }
 
             mActiveColorMode = colorMode;
-            mActiveColorModeInvalid = false;
             getHandler().sendMessage(PooledLambda.obtainMessage(
                     LocalDisplayDevice::requestColorModeAsync, this,
                     getDisplayTokenLocked(), colorMode));
@@ -1052,12 +1050,33 @@
         }
     }
 
-    private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver {
-        PhysicalDisplayEventReceiver(Looper looper) {
-            super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH);
+    public static class Injector {
+        private ProxyDisplayEventReceiver mReceiver;
+        public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
+            mReceiver = new ProxyDisplayEventReceiver(looper, listener);
         }
+    }
 
-        @Override
+    public interface DisplayEventListener {
+        void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+        void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+    }
+
+    public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver {
+        private final DisplayEventListener mListener;
+        ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
+            super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH);
+            mListener = listener;
+        }
+        public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
+            mListener.onHotplug(timestampNanos, physicalDisplayId, connected);
+        }
+        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+            mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+        }
+    }
+
+    private final class LocalDisplayEventListener implements DisplayEventListener {
         public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
             synchronized (getSyncRoot()) {
                 if (connected) {
@@ -1067,8 +1086,6 @@
                 }
             }
         }
-
-        @Override
         public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
             if (DEBUG) {
                 Slog.d(TAG, "onConfigChanged("
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 793cfcd..f997352 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -382,7 +382,7 @@
         if (mPlaybackState == null) {
             return false;
         }
-        return MediaSession.isActiveState(mPlaybackState.getState()) == expected;
+        return mPlaybackState.isActiveState() == expected;
     }
 
     /**
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index cb6e960..eeb2655 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -57,7 +57,7 @@
     private final PackageManagerHelper mPackageManager;
 
     /**
-     * Package name of the reference package defined in 'config-signature' tag of
+     * Package name of the reference package defined in 'overlay-config-signature' tag of
      * SystemConfig or empty String if tag not defined. This package is vetted on scan by
      * PackageManagerService that it's a system package and is used to check if overlay matches
      * its signature in order to fulfill the config_signature policy.
@@ -159,7 +159,7 @@
             fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
         }
 
-        // If SystemConfig defines 'config-signature' package, given that
+        // If SystemConfig defines 'overlay-config-signature' package, given that
         // this package is vetted by OverlayManagerService that it's a
         // preinstalled package, check if overlay matches its signature.
         if (!TextUtils.isEmpty(mConfigSignaturePackage)
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index f8bb564..9bb6f78 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -44,8 +44,6 @@
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageManager;
 import android.content.pm.IShortcutChangeCallback;
-import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.LauncherActivityInfoInternal;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
@@ -371,13 +369,18 @@
             }
         }
 
-        private LauncherActivityInfoInternal getHiddenAppActivityInfo(String packageName,
-                int callingUid, UserHandle user) {
+        private ResolveInfo getHiddenAppActivityInfo(String packageName, int callingUid,
+                UserHandle user) {
             Intent intent = new Intent();
             intent.setComponent(new ComponentName(packageName,
                     PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME));
-            final List<LauncherActivityInfoInternal> apps = queryIntentLauncherActivities(intent,
-                    callingUid, user);
+            final PackageManagerInternal pmInt =
+                    LocalServices.getService(PackageManagerInternal.class);
+            List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                    callingUid, user.getIdentifier());
             if (apps.size() > 0) {
                 return apps.get(0);
             }
@@ -396,14 +399,14 @@
         }
 
         @Override
-        public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
-                String callingPackage, String packageName, UserHandle user) throws RemoteException {
-            ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
-                    queryActivitiesForUser(callingPackage,
-                            new Intent(Intent.ACTION_MAIN)
-                                    .addCategory(Intent.CATEGORY_LAUNCHER)
-                                    .setPackage(packageName),
-                            user);
+        public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
+                String packageName, UserHandle user) throws RemoteException {
+            ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser(
+                    callingPackage,
+                    new Intent(Intent.ACTION_MAIN)
+                            .addCategory(Intent.CATEGORY_LAUNCHER)
+                            .setPackage(packageName),
+                    user);
             if (Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
                 return launcherActivities;
@@ -425,8 +428,7 @@
                     return launcherActivities;
                 }
 
-                final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
-                        launcherActivities.getList());
+                final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
                 if (packageName != null) {
@@ -438,8 +440,7 @@
                     ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
                             callingUid, user.getIdentifier());
                     if (shouldShowSyntheticActivity(user, appInfo)) {
-                        LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
-                                callingUid, user);
+                        ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
                         if (info != null) {
                             result.add(info);
                         }
@@ -447,8 +448,8 @@
                     return new ParceledListSlice<>(result);
                 }
                 final HashSet<String> visiblePackages = new HashSet<>();
-                for (LauncherActivityInfoInternal info : result) {
-                    visiblePackages.add(info.getActivityInfo().packageName);
+                for (ResolveInfo info : result) {
+                    visiblePackages.add(info.activityInfo.packageName);
                 }
                 List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(0,
                         user.getIdentifier(), callingUid);
@@ -457,8 +458,8 @@
                         if (!shouldShowSyntheticActivity(user, applicationInfo)) {
                             continue;
                         }
-                        LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
-                                applicationInfo.packageName, callingUid, user);
+                        ResolveInfo info = getHiddenAppActivityInfo(applicationInfo.packageName,
+                                callingUid, user);
                         if (info != null) {
                             result.add(info);
                         }
@@ -532,7 +533,7 @@
         }
 
         @Override
-        public LauncherActivityInfoInternal resolveLauncherActivityInternal(
+        public ActivityInfo resolveActivity(
                 String callingPackage, ComponentName component, UserHandle user)
                 throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot resolve activity")) {
@@ -544,18 +545,10 @@
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
-                final ActivityInfo activityInfo = pmInt.getActivityInfo(component,
+                return pmInt.getActivityInfo(component,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                         callingUid, user.getIdentifier());
-                final IncrementalStatesInfo incrementalStatesInfo;
-                if (component.getPackageName() == null) {
-                    incrementalStatesInfo = null;
-                } else {
-                    incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
-                            component.getPackageName(), callingUid, user.getIdentifier());
-                }
-                return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -569,47 +562,28 @@
                     new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
         }
 
-        private ParceledListSlice<LauncherActivityInfoInternal> queryActivitiesForUser(
-                String callingPackage, Intent intent, UserHandle user) {
+        private ParceledListSlice<ResolveInfo> queryActivitiesForUser(String callingPackage,
+                Intent intent, UserHandle user) {
             if (!canAccessProfile(user.getIdentifier(), "Cannot retrieve activities")) {
                 return null;
             }
+
             final int callingUid = injectBinderCallingUid();
             long ident = injectClearCallingIdentity();
             try {
-                return new ParceledListSlice<>(queryIntentLauncherActivities(intent, callingUid,
-                        user));
+                final PackageManagerInternal pmInt =
+                        LocalServices.getService(PackageManagerInternal.class);
+                List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                        callingUid, user.getIdentifier());
+                return new ParceledListSlice<>(apps);
             } finally {
                 injectRestoreCallingIdentity(ident);
             }
         }
 
-        private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
-                Intent intent, int callingUid, UserHandle user) {
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                    callingUid, user.getIdentifier());
-            final int numResolveInfos = apps.size();
-            List<LauncherActivityInfoInternal> results = new ArrayList<>();
-            for (int i = 0; i < numResolveInfos; i++) {
-                final ResolveInfo ri = apps.get(i);
-                final IncrementalStatesInfo incrementalStatesInfo;
-                if (ri.resolvePackageName == null) {
-                    incrementalStatesInfo = null;
-                } else {
-                    incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
-                            ri.resolvePackageName, callingUid, user.getIdentifier());
-                }
-                results.add(new LauncherActivityInfoInternal(ri.activityInfo,
-                        incrementalStatesInfo));
-            }
-            return results;
-        }
-
         @Override
         public IntentSender getShortcutConfigActivityIntent(String callingPackage,
                 ComponentName component, UserHandle user) throws RemoteException {
@@ -1264,10 +1238,7 @@
                 } finally {
                     mListeners.finishBroadcast();
                 }
-                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-                pmi.registerInstalledLoadingProgressCallback(packageName,
-                        new PackageLoadingProgressCallback(packageName, user),
-                        user.getIdentifier());
+
                 super.onPackageAdded(packageName, uid);
             }
 
@@ -1295,11 +1266,6 @@
 
             @Override
             public void onPackageModified(String packageName) {
-                onPackageChanged(packageName);
-                super.onPackageModified(packageName);
-            }
-
-            private void onPackageChanged(String packageName) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
                 try {
@@ -1316,6 +1282,8 @@
                 } finally {
                     mListeners.finishBroadcast();
                 }
+
+                super.onPackageModified(packageName);
             }
 
             @Override
@@ -1467,7 +1435,6 @@
                         } catch (RemoteException re) {
                             Slog.d(TAG, "Callback failed ", re);
                         }
-
                     }
                 } catch (RuntimeException e) {
                     // When the user is locked we get IllegalState, so just catch all.
@@ -1476,12 +1443,6 @@
                     mListeners.finishBroadcast();
                 }
             }
-
-            @Override
-            public void onPackageStateChanged(String packageName, int uid) {
-                onPackageChanged(packageName);
-                super.onPackageStateChanged(packageName, uid);
-            }
         }
 
         class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
@@ -1490,38 +1451,5 @@
                 checkCallbackCount();
             }
         }
-
-        class PackageLoadingProgressCallback extends
-                PackageManagerInternal.InstalledLoadingProgressCallback {
-            private String mPackageName;
-            private UserHandle mUser;
-
-            PackageLoadingProgressCallback(String packageName, UserHandle user) {
-                super(mCallbackHandler);
-                mPackageName = packageName;
-                mUser = user;
-            }
-
-            @Override
-            public void onLoadingProgressChanged(float progress) {
-                final int n = mListeners.beginBroadcast();
-                try {
-                    for (int i = 0; i < n; i++) {
-                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
-                            continue;
-                        }
-                        try {
-                            listener.onPackageProgressChanged(mUser, mPackageName, progress);
-                        } catch (RemoteException re) {
-                            Slog.d(TAG, "Callback failed ", re);
-                        }
-                    }
-                } finally {
-                    mListeners.finishBroadcast();
-                }
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 39423c7..ff84e2e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -180,7 +180,6 @@
 import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
-import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.InstantAppRequest;
@@ -12350,9 +12349,10 @@
                     // A non-preloaded overlay package, without <overlay android:targetName>, will
                     // only be used if it is signed with the same certificate as its target OR if
                     // it is signed with the same certificate as a reference package declared
-                    // in 'config-signature' tag of SystemConfig.
-                    // If the target is already installed or 'config-signature' tag in SystemConfig
-                    // is set, check this here to augment the last line of defence which is OMS.
+                    // in 'overlay-config-signature' tag of SystemConfig.
+                    // If the target is already installed or 'overlay-config-signature' tag in
+                    // SystemConfig is set, check this here to augment the last line of defense
+                    // which is OMS.
                     if (pkg.getOverlayTargetName() == null) {
                         final PackageSetting targetPkgSetting =
                                 mSettings.getPackageLPr(pkg.getOverlayTarget());
@@ -25659,20 +25659,8 @@
             return mIncrementalManager.unregisterCallback(ps.getPathString(),
                     (IPackageLoadingProgressCallback) callback.getBinder());
         }
-
-        @Override
-        public IncrementalStatesInfo getIncrementalStatesInfo(
-                @NonNull String packageName, int filterCallingUid, int userId) {
-            final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
-                    userId);
-            if (ps == null) {
-                return null;
-            }
-            return ps.getIncrementalStates();
-        }
     }
 
-
     @GuardedBy("mLock")
     private SparseArray<String> getAppsWithSharedUserIdsLocked() {
         final SparseArray<String> sharedUserIds = new SparseArray<>();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 766b30f..66e84b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3906,7 +3906,7 @@
      */
     @Override
     public boolean removeUser(@UserIdInt int userId) {
-        Slog.i(LOG_TAG, "removeUser u" + userId);
+        Slog.i(LOG_TAG, "removeUser u" + userId, new Exception());
         checkManageOrCreateUsersPermission("Only the system can remove users");
 
         final String restriction = getUserRemovalRestriction(userId);
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index bebb676..667414e 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -198,6 +198,11 @@
                 == PermissionInfo.PROTECTION_DANGEROUS;
     }
 
+    public boolean isInstalled() {
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0;
+    }
+
     public boolean isRemoved() {
         return mPermissionInfo != null
                 && (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 19a5650..d871325 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2352,19 +2352,23 @@
 
                 final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p,
                         PackageManager.GET_META_DATA);
+                final BasePermission bp;
                 if (p.isTree()) {
-                    final BasePermission bp = BasePermission.createOrUpdate(
+                    bp = BasePermission.createOrUpdate(
                             mPackageManagerInt,
                             mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg,
                             mSettings.getAllPermissionTreesLocked(), chatty);
                     mSettings.putPermissionTreeLocked(p.getName(), bp);
                 } else {
-                    final BasePermission bp = BasePermission.createOrUpdate(
+                    bp = BasePermission.createOrUpdate(
                             mPackageManagerInt,
                             mSettings.getPermissionLocked(p.getName()),
                             permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
                     mSettings.putPermissionLocked(p.getName(), bp);
                 }
+                if (bp.isInstalled()) {
+                    p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9350edf..1e89e06 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -28,7 +28,6 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
@@ -1089,8 +1088,8 @@
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
                 installerPackageName) == PackageManager.PERMISSION_GRANTED;
 
-        // For now only allow rollbacks for modules or for testing.
-        return (isRollbackWhitelisted(packageName) && manageRollbacksGranted)
+        // For now only allow rollbacks for allowlisted packages or for testing.
+        return (isRollbackAllowlisted(packageName) && manageRollbacksGranted)
             || testManageRollbacksGranted;
     }
 
@@ -1098,25 +1097,8 @@
      * Returns true is this package is eligible for enabling rollback.
      */
     @AnyThread
-    private boolean isRollbackWhitelisted(String packageName) {
-        // TODO: Remove #isModule when the allowlist is ready.
-        return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
-                || isModule(packageName);
-    }
-    /**
-     * Returns true if the package name is the name of a module.
-     */
-    @AnyThread
-    private boolean isModule(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        final ModuleInfo moduleInfo;
-        try {
-            moduleInfo = pm.getModuleInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-
-        return moduleInfo != null;
+    private boolean isRollbackAllowlisted(String packageName) {
+        return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 48b0b7d..99b017f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5572,7 +5572,10 @@
     }
 
     void updateTopApp(ActivityRecord topResumedActivity) {
-        final ActivityRecord top = topResumedActivity != null ? topResumedActivity
+        // If system is sleeping, use the given record (it should be null) because there won't be
+        // the next resumed activity. Otherwise the process of pausing activity will keep with top
+        // state even the activity has paused and stopped.
+        final ActivityRecord top = mSleeping || topResumedActivity != null ? topResumedActivity
                 // If there is no resumed activity, it will choose the pausing activity.
                 : mRootWindowContainer.getTopResumedActivity();
         mTopApp = top != null ? top.app : null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fed99a4..4a30ca9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -96,6 +96,7 @@
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_READY;
+import static com.android.server.wm.DisplayContentProto.DISPLAY_ROTATION;
 import static com.android.server.wm.DisplayContentProto.DPI;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
@@ -107,7 +108,6 @@
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
-import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
@@ -192,6 +192,8 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputWindowHandle;
+import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
@@ -2871,7 +2873,7 @@
         proto.write(ID, mDisplayId);
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.dumpDebug(proto, DISPLAY_INFO);
-        proto.write(ROTATION, getRotation());
+        mDisplayRotation.dumpDebug(proto, DISPLAY_ROTATION);
         final ScreenRotationAnimation screenRotationAnimation = getRotationAnimation();
         if (screenRotationAnimation != null) {
             screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
@@ -4830,7 +4832,7 @@
             boolean ignoreRequest) {
         final int type = win.mAttrs.type;
         final boolean stickyHideNav =
-                !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                         && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
         return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD
                 && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME;
@@ -5557,6 +5559,7 @@
 
     class RemoteInsetsControlTarget implements InsetsControlTarget {
         private final IDisplayWindowInsetsController mRemoteInsetsController;
+        private final InsetsState mRequestedInsetsState = new InsetsState();
 
         RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
             mRemoteInsetsController = controller;
@@ -5612,6 +5615,19 @@
                 Slog.w(TAG, "Failed to deliver showInsets", e);
             }
         }
+
+        @Override
+        public boolean getRequestedVisibility(@InternalInsetsType int type) {
+            return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
+        }
+
+        void updateRequestedVisibility(InsetsState state) {
+            for (int i = 0; i < InsetsState.SIZE; i++) {
+                final InsetsSource source = state.peekSource(i);
+                if (source == null) continue;
+                mRequestedInsetsState.addSource(source);
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4e7e0ba..0801f68 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1659,10 +1659,8 @@
                 provider != null ? provider.getControlTarget() : null;
         final WindowState navControllingWin =
                 navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null;
-        final InsetsState requestedState = navControllingWin != null
-                ? navControllingWin.getRequestedInsetsState() : null;
-        final boolean navVisible = requestedState != null
-                ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+        final boolean navVisible = navControllingWin != null
+                ? navControllingWin.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                 : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
         final boolean showBarsByTouch = navControllingWin != null
                 && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH;
@@ -2073,11 +2071,9 @@
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
             final boolean attachedInParent = attached != null && !layoutInScreen;
-            final InsetsState requestedInsetsState = win.getRequestedInsetsState();
-            final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
-                    || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR);
+            final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
             final boolean requestedHideNavigation =
-                    !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+                    !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
 
             // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
             // get cropped / shifted to the displayFrame in WindowState.
@@ -2377,7 +2373,7 @@
                 topIsFullscreen = topAppHidesStatusBar;
                 // The subtle difference between the window for mTopFullscreenOpaqueWindowState
                 // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
-                // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
+                // requests to hide the status bar.  Not sure if there is another way that to be the
                 // case though.
                 if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
                         .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
@@ -2421,16 +2417,7 @@
         if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) {
             return false;
         }
-        final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs();
-        final int fl = attrs.flags;
-        final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState()
-                .peekSource(ITYPE_STATUS_BAR);
-        if (WindowManagerDebugConfig.DEBUG) {
-            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrame());
-            Slog.d(TAG, "attr: " + attrs + " request: " + request);
-        }
-        return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
-                || (request != null && !request.isVisible());
+        return !mTopFullscreenOpaqueWindowState.getRequestedVisibility(ITYPE_STATUS_BAR);
     }
 
     /**
@@ -2864,18 +2851,15 @@
             return;
         }
 
-        final InsetsState requestedState = controlTarget.getRequestedInsetsState();
         final @InsetsType int restorePositionTypes =
-                (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                (controlTarget.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                         ? Type.navigationBars() : 0)
-                | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+                | (controlTarget.getRequestedVisibility(ITYPE_STATUS_BAR)
                         ? Type.statusBars() : 0)
-                | (mExtraNavBarAlt != null
-                        && requestedState.getSourceOrDefaultVisibility(
+                | (mExtraNavBarAlt != null && controlTarget.getRequestedVisibility(
                                 ITYPE_EXTRA_NAVIGATION_BAR)
                         ? Type.navigationBars() : 0)
-                | (mClimateBarAlt != null
-                        && requestedState.getSourceOrDefaultVisibility(
+                | (mClimateBarAlt != null && controlTarget.getRequestedVisibility(
                                 ITYPE_CLIMATE_BAR)
                         ? Type.statusBars() : 0);
 
@@ -2981,12 +2965,11 @@
                 win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
                 mTopFullscreenOpaqueOrDimmingWindowState,
                 mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
-        final InsetsState requestedInsets = win.getRequestedInsetsState();
         final int behavior = win.mAttrs.insetsFlags.behavior;
         final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
                 || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
-        final boolean isFullscreen = !requestedInsets.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
-                || !requestedInsets.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
+                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
         if (mLastDisableFlags == disableFlags
                 && mLastAppearance == appearance
                 && mLastFullscreenAppearance == fullscreenAppearance
@@ -3152,10 +3135,7 @@
                 freeformStackVisible, resizing, fullscreenDrawsNavBarBackground,
                 dockedDrawsNavigationBarBackground);
 
-        final InsetsState requestedInsetsState = win.getRequestedInsetsState();
-        final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility(
-                        ITYPE_NAVIGATION_BAR);
-
+        final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
         final long now = SystemClock.uptimeMillis();
         final boolean pendingPanic = mPendingPanicGestureUptime != 0
                 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0f43e49..c503431 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -24,6 +24,11 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
+import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
+import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
+import static com.android.server.wm.DisplayRotationProto.ROTATION;
+import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -51,6 +56,7 @@
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IWindowManager;
 import android.view.Surface;
@@ -847,6 +853,10 @@
         }
     }
 
+    int getFixedToUserRotationMode() {
+        return mFixedToUserRotation;
+    }
+
     /**
      * Returns {@code true} if this display rotation takes app requested orientation into
      * consideration; {@code false} otherwise. For the time being the only case where this is {@code
@@ -1453,6 +1463,16 @@
         pw.println(prefix + "  mFixedToUserRotation=" + isFixedToUserRotation());
     }
 
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ROTATION, getRotation());
+        proto.write(FROZEN_TO_USER_ROTATION, isRotationFrozen());
+        proto.write(USER_ROTATION, getUserRotation());
+        proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
+        proto.write(LAST_ORIENTATION, mLastOrientation);
+        proto.end(token);
+    }
+
     private class OrientationListener extends WindowOrientationListener {
         final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
         boolean mEnabled;
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 5e7ed3f..287dd74 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -18,6 +18,7 @@
 
 import android.inputmethodservice.InputMethodService;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsets.Type.InsetsType;
 
 /**
@@ -39,10 +40,10 @@
     }
 
     /**
-     * @return The requested {@link InsetsState} of this target.
+     * @return The requested visibility of this target.
      */
-    default InsetsState getRequestedInsetsState() {
-        return InsetsState.EMPTY;
+    default boolean getRequestedVisibility(@InternalInsetsType int type) {
+        return InsetsState.getDefaultVisibility(type);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3617884..bd05da9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -173,13 +173,7 @@
             // animation frame which will be triggered if a new leash is created.
             mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                    final InsetsState state = new InsetsState(mStateController.getRawInsetsState());
-                    startAnimation(true /* show */, () -> {
-                        synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                            mStateController.notifyInsetsChanged();
-                        }
-                    }, state);
-                    mStateController.onInsetsModified(mDummyControlTarget, state);
+                    startAnimation(true /* show */, null /* callback */);
                 }
             });
         }
@@ -189,15 +183,18 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
-        InsetsState state = new InsetsState(mStateController.getRawInsetsState());
         startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
+                for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+                    // We are about to clear mShowingTransientTypes, we don't want the transient bar
+                    // can cause insets on the client. Restore the client visibility.
+                    final @InternalInsetsType int type = mShowingTransientTypes.get(i);
+                    mStateController.getSourceProvider(type).setClientVisible(false);
+                }
                 mShowingTransientTypes.clear();
-                mStateController.notifyInsetsChanged();
                 updateBarControlTarget(mFocusedWin);
             }
-        }, state);
-        mStateController.onInsetsModified(mDummyControlTarget, state);
+        });
     }
 
     boolean isTransient(@InternalInsetsType int type) {
@@ -211,7 +208,7 @@
         final InsetsState originalState = mStateController.getInsetsForDispatch(target);
         InsetsState state = originalState;
         for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
-            final int type = mShowingTransientTypes.get(i);
+            final @InternalInsetsType int type = mShowingTransientTypes.get(i);
             final InsetsSource originalSource = state.peekSource(type);
             if (originalSource != null && originalSource.isVisible()) {
                 if (state == originalState) {
@@ -227,26 +224,25 @@
         return state;
     }
 
-    void onInsetsModified(WindowState windowState, InsetsState state) {
-        mStateController.onInsetsModified(windowState, state);
-        checkAbortTransient(windowState, state);
+    void onInsetsModified(InsetsControlTarget caller) {
+        mStateController.onInsetsModified(caller);
+        checkAbortTransient(caller);
         updateBarControlTarget(mFocusedWin);
     }
 
     /**
-     * Called when a window modified the insets state. If the window set a insets source to visible
-     * while it is shown transiently, we need to abort the transient state.
+     * Called when a control target modified the insets state. If the target set a insets source to
+     * visible while it is shown transiently, we need to abort the transient state.
      *
-     * @param windowState who changed the insets state.
-     * @param state the modified insets state.
+     * @param caller who changed the insets state.
      */
-    private void checkAbortTransient(WindowState windowState, InsetsState state) {
+    private void checkAbortTransient(InsetsControlTarget caller) {
         if (mShowingTransientTypes.size() != 0) {
             IntArray abortTypes = new IntArray();
             for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
-                final int type = mShowingTransientTypes.get(i);
-                if (mStateController.isFakeTarget(type, windowState)
-                        && state.getSource(type).isVisible()) {
+                final @InternalInsetsType int type = mShowingTransientTypes.get(i);
+                if (mStateController.isFakeTarget(type, caller)
+                        && caller.getRequestedVisibility(type)) {
                     mShowingTransientTypes.remove(i);
                     abortTypes.add(type);
                 }
@@ -393,12 +389,12 @@
     }
 
     @VisibleForTesting
-    void startAnimation(boolean show, Runnable callback, InsetsState state) {
+    void startAnimation(boolean show, Runnable callback) {
         int typesReady = 0;
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
         final IntArray showingTransientTypes = mShowingTransientTypes;
         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
-            int type = showingTransientTypes.get(i);
+            final @InternalInsetsType int type = showingTransientTypes.get(i);
             InsetsSourceProvider provider = mStateController.getSourceProvider(type);
             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
             if (control == null || control.getLeash() == null) {
@@ -406,7 +402,6 @@
             }
             typesReady |= InsetsState.toPublicType(type);
             controls.put(control.getType(), new InsetsSourceControl(control));
-            state.setSourceVisible(type, show);
         }
         controlAnimationUnchecked(typesReady, controls, show, callback);
     }
@@ -428,12 +423,9 @@
             mId = id;
         }
 
-        private void updateVisibility(InsetsControlTarget controlTarget,
+        private void updateVisibility(@Nullable InsetsControlTarget controlTarget,
                 @InternalInsetsType int type) {
-            final WindowState controllingWin =
-                    controlTarget instanceof WindowState ? (WindowState) controlTarget : null;
-            setVisible(controllingWin == null
-                    || controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type));
+            setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type));
         }
 
         private void setVisible(boolean visible) {
@@ -463,7 +455,9 @@
         @Override
         protected void onAnimationFinish() {
             super.onAnimationFinish();
-            DisplayThread.getHandler().post(mFinishCallback);
+            if (mFinishCallback != null) {
+                DisplayThread.getHandler().post(mFinishCallback);
+            }
         }
 
         private class InsetsPolicyAnimationControlCallbacks implements
@@ -484,7 +478,7 @@
                 mAnimatingShown = show;
 
                 mAnimationControl = new InsetsAnimationControlImpl(controls,
-                        mFocusedWin.getDisplayContent().getBounds(), getState(),
+                        mFocusedWin.getDisplayContent().getBounds(), mFocusedWin.getInsetsState(),
                         mListener, typesReady, this, mListener.getDurationMs(),
                         InsetsController.SYSTEM_BARS_INTERPOLATOR,
                         show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
@@ -495,8 +489,7 @@
             /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
-                InsetsState state = getState();
-                if (mAnimationControl.applyChangeInsets(state)) {
+                if (mAnimationControl.applyChangeInsets(null /* outState */)) {
                     mAnimationControl.finish(mAnimatingShown);
                 }
             }
@@ -507,20 +500,6 @@
                 // onAnimationFinished callback.
             }
 
-            /**
-             * This method will return a state with fullscreen frame override. No need to make copy
-             * after getting state from this method.
-             * @return The client insets state with full display frame override.
-             */
-            private InsetsState getState() {
-                // To animate the transient animation correctly, we need to let the state hold
-                // the full display frame.
-                InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(),
-                        true);
-                overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds());
-                return overrideState;
-            }
-
             /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void applySurfaceParams(
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7defc5d..e83151d 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -338,11 +338,12 @@
         }
     }
 
-    boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) {
-        if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
+    boolean updateClientVisibility(InsetsControlTarget caller) {
+        final boolean requestedVisible = caller.getRequestedVisibility(mSource.getType());
+        if (caller != mControlTarget || requestedVisible == mClientVisible) {
             return false;
         }
-        setClientVisible(modifiedSource.isVisible());
+        setClientVisible(requestedVisible);
         return true;
     }
 
@@ -350,7 +351,7 @@
         mIsLeashReadyForDispatching = true;
     }
 
-    private void setClientVisible(boolean clientVisible) {
+    void setClientVisible(boolean clientVisible) {
         if (mClientVisible == clientVisible) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b59452f..b9c2093 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -289,16 +289,10 @@
         winInsetsChanged.clear();
     }
 
-    void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
+    void onInsetsModified(InsetsControlTarget caller) {
         boolean changed = false;
-        for (int i = 0; i < InsetsState.SIZE; i++) {
-            final InsetsSource source = state.peekSource(i);
-            if (source == null) continue;
-            final InsetsSourceProvider provider = mProviders.get(source.getType());
-            if (provider == null) {
-                continue;
-            }
-            changed |= provider.onInsetsModified(windowState, source);
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            changed |= mProviders.valueAt(i).updateClientVisibility(caller);
         }
         if (changed) {
             notifyInsetsChanged();
@@ -464,11 +458,24 @@
                 final InsetsSourceProvider provider = mProviders.valueAt(i);
                 provider.onSurfaceTransactionApplied();
             }
+            final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
             for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
                 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
                 controlTarget.notifyInsetsControlChanged();
+                if (mControlTargetTypeMap.containsKey(controlTarget)) {
+                    // We only collect targets who get controls, not lose controls.
+                    newControlTargets.add(controlTarget);
+                }
             }
             mPendingControlChanged.clear();
+
+            // This updates the insets visibilities AFTER sending current insets state and controls
+            // to the clients, so that the clients can change the current visibilities to the
+            // requested visibilities with animations.
+            for (int i = newControlTargets.size() - 1; i >= 0; i--) {
+                onInsetsModified(newControlTargets.valueAt(i));
+            }
+            newControlTargets.clear();
         });
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 02230d6..1b887a7 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -84,6 +84,9 @@
     private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
     private final DragDropController mDragDropController;
     final boolean mCanAddInternalSystemWindow;
+    // If non-system overlays from this process can be hidden by the user or app using
+    // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
+    final boolean mOverlaysCanBeHidden;
     final boolean mCanHideNonSystemOverlayWindows;
     final boolean mCanAcquireSleepToken;
     private AlertWindowNotification mAlertWindowNotification;
@@ -92,6 +95,7 @@
     private float mLastReportedAnimatorScale;
     private String mPackageName;
     private String mRelayoutTag;
+    private final InsetsState mDummyRequestedVisibility = new InsetsState();
     private final InsetsSourceControl[] mDummyControls =  new InsetsSourceControl[0];
 
     public Session(WindowManagerService service, IWindowSessionCallback callback) {
@@ -104,6 +108,8 @@
                 INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
         mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission(
                 HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED;
+        mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
+                && !mService.mAtmInternal.isCallerRecents(mUid);
         mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
                 == PERMISSION_GRANTED;
         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -158,25 +164,26 @@
 
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
-            Rect outStableInsets,
+            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+            Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
+        return mService.addWindow(this, window, attrs, viewVisibility, displayId,
+                UserHandle.getUserId(mUid), requestedVisibility, outFrame,
                 outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState, outActiveControls, UserHandle.getUserId(mUid));
+                outInsetsState, outActiveControls);
     }
 
 
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, int userId, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
+            int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+            Rect outFrame, Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState, outActiveControls, userId);
+        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
+                requestedVisibility, outFrame, outContentInsets, outStableInsets, outDisplayCutout,
+                outInputChannel, outInsetsState, outActiveControls);
     }
 
     @Override
@@ -184,9 +191,10 @@
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
             InsetsState outInsetsState) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
+                UserHandle.getUserId(mUid), mDummyRequestedVisibility,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets,
                 new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
-                outInsetsState, mDummyControls, UserHandle.getUserId(mUid));
+                outInsetsState, mDummyControls);
     }
 
     @Override
@@ -493,9 +501,8 @@
             final WindowState windowState = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (windowState != null) {
-                windowState.updateRequestedInsetsState(state);
-                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
-                        windowState, state);
+                windowState.updateRequestedVisibility(state);
+                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState);
             }
         }
     }
@@ -532,7 +539,7 @@
 
         boolean changed;
 
-        if (!mCanAddInternalSystemWindow) {
+        if (mOverlaysCanBeHidden) {
             // We want to track non-system signature apps adding alert windows so we can post an
             // on-going notification for the user to control their visibility.
             if (visible) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 87b470a..01adb8b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -482,8 +482,7 @@
         final int color = ColorUtils.setAlphaComponent(
                 task.getTaskDescription().getBackgroundColor(), 255);
         final LayoutParams attrs = mainWindow.getAttrs();
-        final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState());
-        mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
+        final InsetsState insetsState = getInsetsStateWithVisibilityOverride(mainWindow);
         final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
@@ -603,13 +602,18 @@
         return 0;
     }
 
-    static void mergeInsetsSources(InsetsState base, InsetsState other) {
+    static InsetsState getInsetsStateWithVisibilityOverride(WindowState win) {
+        final InsetsState state = new InsetsState(win.getInsetsState());
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
-            final InsetsSource source = other.peekSource(type);
-            if (source != null) {
-                base.addSource(source);
+            final boolean requestedVisible = win.getRequestedVisibility(type);
+            InsetsSource source = state.peekSource(type);
+            if (source != null && source.isVisible() != requestedVisible) {
+                source = new InsetsSource(source);
+                source.setVisible(requestedVisible);
+                state.addSource(source);
             }
         }
+        return state;
     }
 
     static Rect getSystemBarInsets(Rect frame, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index efa0525..e8c4491 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,8 +42,8 @@
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.TaskSnapshotController.getInsetsStateWithVisibilityOverride;
 import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
-import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -236,14 +236,15 @@
             task.getBounds(taskBounds);
             currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
             activityType = activity.getActivityType();
-            insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState());
-            mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
+            insetsState = getInsetsStateWithVisibilityOverride(topFullscreenOpaqueWindow);
+
         }
         try {
             final int res = session.addToDisplay(window, layoutParams,
-                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame,
-                    tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout,
-                    null /* outInputChannel */, mTmpInsetsState, mTempControls);
+                    View.GONE, activity.getDisplayContent().getDisplayId(), mTmpInsetsState,
+                    tmpFrames.frame, tmpFrames.contentInsets, tmpFrames.stableInsets,
+                    tmpFrames.displayCutout, null /* outInputChannel */, mTmpInsetsState,
+                    mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4be118e..40b770f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -122,10 +122,8 @@
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
 import static com.android.server.wm.WindowManagerServiceDumpProto.HARD_KEYBOARD_AVAILABLE;
 import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
-import static com.android.server.wm.WindowManagerServiceDumpProto.LAST_ORIENTATION;
 import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
 import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
-import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -1377,10 +1375,10 @@
     }
 
     public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
-            int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
+            int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
+            Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
-            int requestUserId) {
+            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         Arrays.fill(outActiveControls, null);
         int[] appOp = new int[1];
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
@@ -1575,6 +1573,7 @@
 
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
             displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
+            win.updateRequestedVisibility(requestedVisibility);
 
             res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
             if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -3717,7 +3716,7 @@
             synchronized (mGlobalLock) {
                 final DisplayContent display = mRoot.getDisplayContent(displayId);
                 if (display == null) {
-                    Slog.w(TAG, "Trying to set rotate for app for a missing display.");
+                    Slog.w(TAG, "Trying to set fixed to user rotation for a missing display.");
                     return;
                 }
                 display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
@@ -3727,6 +3726,17 @@
         }
     }
 
+    int getFixedToUserRotation(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to get fixed to user rotation for a missing display.");
+                return -1;
+            }
+            return display.getDisplayRotation().getFixedToUserRotationMode();
+        }
+    }
+
     @Override
     public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(
@@ -3843,13 +3853,24 @@
         synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
-                Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+                Slog.w(TAG, "Trying to check if rotation is frozen on a missing display.");
                 return false;
             }
             return display.getDisplayRotation().isRotationFrozen();
         }
     }
 
+    int getDisplayUserRotation(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to get user rotation of a missing display.");
+                return -1;
+            }
+            return display.getDisplayRotation().getUserRotation();
+        }
+    }
+
     /**
      * Recalculate the current rotation.
      *
@@ -4014,8 +4035,8 @@
                 if (dc == null || dc.mRemoteInsetsControlTarget == null) {
                     return;
                 }
-                dc.getInsetsStateController().onInsetsModified(
-                        dc.mRemoteInsetsControlTarget, state);
+                dc.mRemoteInsetsControlTarget.updateRequestedVisibility(state);
+                dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -6029,8 +6050,6 @@
         }
         proto.write(DISPLAY_FROZEN, mDisplayFrozen);
         final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
-        proto.write(ROTATION, defaultDisplayContent.getRotation());
-        proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
         proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId());
         proto.write(HARD_KEYBOARD_AVAILABLE, mHardKeyboardAvailable);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index fa1c50f..3011f25 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -28,7 +28,6 @@
 import android.util.Pair;
 import android.view.Display;
 import android.view.IWindowManager;
-import android.view.Surface;
 import android.view.ViewDebug;
 
 import com.android.internal.os.ByteTransferPipe;
@@ -102,10 +101,10 @@
                         }
                     }
                     return result;
-                case "set-user-rotation":
-                    return runSetDisplayUserRotation(pw);
-                case "set-fix-to-user-rotation":
-                    return runSetFixToUserRotation(pw);
+                case "user-rotation":
+                    return runDisplayUserRotation(pw);
+                case "fixed-to-user-rotation":
+                    return runFixedToUserRotation(pw);
                 case "set-ignore-orientation-request":
                     return runSetIgnoreOrientationRequest(pw);
                 case "get-ignore-orientation-request":
@@ -313,28 +312,37 @@
         return Integer.parseInt(s);
     }
 
-    private int runSetDisplayUserRotation(PrintWriter pw) {
-        final String lockMode = getNextArgRequired();
-
+    private int runDisplayUserRotation(PrintWriter pw) {
         int displayId = Display.DEFAULT_DISPLAY;
         String arg = getNextArg();
+        if (arg == null) {
+            return printDisplayUserRotation(pw, displayId);
+        }
+
         if ("-d".equals(arg)) {
             displayId = Integer.parseInt(getNextArgRequired());
             arg = getNextArg();
         }
 
+        final String lockMode = arg;
+        if (lockMode == null) {
+            return printDisplayUserRotation(pw, displayId);
+        }
+
         if ("free".equals(lockMode)) {
             mInternal.thawDisplayRotation(displayId);
             return 0;
         }
 
-        if (!lockMode.equals("lock")) {
-            getErrPrintWriter().println("Error: lock mode needs to be either free or lock.");
+        if (!"lock".equals(lockMode)) {
+            getErrPrintWriter().println("Error: argument needs to be either -d, free or lock.");
             return -1;
         }
 
+        arg = getNextArg();
         try {
-            final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0;
+            final int rotation =
+                    arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */;
             mInternal.freezeDisplayRotation(displayId, rotation);
             return 0;
         } catch (IllegalArgumentException e) {
@@ -343,12 +351,36 @@
         }
     }
 
-    private int runSetFixToUserRotation(PrintWriter pw) throws RemoteException {
+    private int printDisplayUserRotation(PrintWriter pw, int displayId) {
+        final int displayUserRotation = mInternal.getDisplayUserRotation(displayId);
+        if (displayUserRotation < 0) {
+            getErrPrintWriter().println("Error: check logcat for more details.");
+            return -1;
+        }
+        if (!mInternal.isDisplayRotationFrozen(displayId)) {
+            pw.println("free");
+            return 0;
+        }
+        pw.print("lock ");
+        pw.println(displayUserRotation);
+        return 0;
+    }
+
+    private int runFixedToUserRotation(PrintWriter pw) throws RemoteException {
         int displayId = Display.DEFAULT_DISPLAY;
-        String arg = getNextArgRequired();
+        String arg = getNextArg();
+        if (arg == null) {
+            printFixedToUserRotation(pw, displayId);
+            return 0;
+        }
+
         if ("-d".equals(arg)) {
             displayId = Integer.parseInt(getNextArgRequired());
-            arg = getNextArgRequired();
+            arg = getNextArg();
+        }
+
+        if (arg == null) {
+            return printFixedToUserRotation(pw, displayId);
         }
 
         final int fixedToUserRotation;
@@ -372,6 +404,24 @@
         return 0;
     }
 
+    private int printFixedToUserRotation(PrintWriter pw, int displayId) {
+        int fixedToUserRotationMode = mInternal.getFixedToUserRotation(displayId);
+        switch (fixedToUserRotationMode) {
+            case IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT:
+                pw.println("default");
+                return 0;
+            case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
+                pw.println("disabled");
+                return 0;
+            case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
+                pw.println("enabled");
+                return 0;
+            default:
+                getErrPrintWriter().println("Error: check logcat for more details.");
+                return -1;
+        }
+    }
+
     private int runSetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException {
         int displayId = Display.DEFAULT_DISPLAY;
         String arg = getNextArgRequired();
@@ -474,12 +524,12 @@
         pw.println("    Set display scaling mode.");
         pw.println("  dismiss-keyguard");
         pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
-        pw.println("  set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
-        pw.println("    Set user rotation mode and user rotation.");
+        pw.println("  user-rotation [-d DISPLAY_ID] [free|lock] [rotation]");
+        pw.println("    Print or set user rotation mode and user rotation.");
         pw.println("  dump-visible-window-views");
         pw.println("    Dumps the encoded view hierarchies of visible windows");
-        pw.println("  set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled|default]");
-        pw.println("    Enable or disable rotating display for app requested orientation.");
+        pw.println("  fixed-to-user-rotation [-d DISPLAY_ID] [enabled|disabled|default]");
+        pw.println("    Print or set rotating display for app requested orientation.");
         pw.println("  set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
         pw.println("  get-ignore-orientation-request [-d DISPLAY_ID] ");
         pw.println("    If app requested orientation should be ignored.");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index e8a7a9c..2e7905c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1257,20 +1257,27 @@
         mAtm.mStackSupervisor.removeHistoryRecords(this);
 
         boolean hasVisibleActivities = false;
-        if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) {
+        final boolean hasInactiveActivities =
+                mInactiveActivities != null && !mInactiveActivities.isEmpty();
+        final ArrayList<ActivityRecord> activities =
+                (mHasActivities || hasInactiveActivities) ? new ArrayList<>() : mActivities;
+        if (mHasActivities) {
+            activities.addAll(mActivities);
+        }
+        if (hasInactiveActivities) {
             // Make sure that all activities in this process are handled.
-            mActivities.addAll(mInactiveActivities);
+            activities.addAll(mInactiveActivities);
         }
         if (isRemoved()) {
             // The package of the died process should be force-stopped, so make its activities as
             // finishing to prevent the process from being started again if the next top (or being
             // visible) activity also resides in the same process. This must be done before removal.
-            for (int i = mActivities.size() - 1; i >= 0; i--) {
-                mActivities.get(i).makeFinishingLocked();
+            for (int i = activities.size() - 1; i >= 0; i--) {
+                activities.get(i).makeFinishingLocked();
             }
         }
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = activities.get(i);
             if (r.mVisibleRequested || r.isVisible()) {
                 // While an activity launches a new activity, it's possible that the old activity
                 // is already requested to be hidden (mVisibleRequested=false), but this visibility
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 25b4828..d4b6d00 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -224,6 +224,7 @@
 import android.view.InputWindowHandle;
 import android.view.InsetsSource;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -719,20 +720,20 @@
     private final WindowProcessController mWpcForDisplayConfigChanges;
 
     /**
-     * @return The insets state as requested by the client, i.e. the dispatched insets state
-     *         for which the visibilities are overridden with what the client requested.
+     * Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
+     *
+     * @param type the given {@link InternalInsetsType type}.
+     * @return {@code true} if the type is requested visible.
      */
     @Override
-    public InsetsState getRequestedInsetsState() {
-        return mRequestedInsetsState;
+    public boolean getRequestedVisibility(@InternalInsetsType int type) {
+        return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
     }
 
     /**
-     * @see #getRequestedInsetsState()
+     * @see #getRequestedVisibility(int)
      */
-    void updateRequestedInsetsState(InsetsState state) {
-
-        // Only update the sources the client is actually controlling.
+    void updateRequestedVisibility(InsetsState state) {
         for (int i = 0; i < InsetsState.SIZE; i++) {
             final InsetsSource source = state.peekSource(i);
             if (source == null) continue;
@@ -3056,7 +3057,7 @@
     }
 
     void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
-        if (mOwnerCanAddInternalSystemWindow
+        if (!mSession.mOverlaysCanBeHidden
                 || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
             return;
         }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 30f6fa6..d6a56ba 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -21,6 +21,7 @@
         "BroadcastRadio/TunerCallback.cpp",
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
+        "gnss/GnssConfiguration.cpp",
         "stats/PowerStatsPuller.cpp",
         "stats/SubsystemSleepStatePuller.cpp",
         "com_android_server_adb_AdbDebuggingManager.cpp",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 91645ba..e9d048a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "GnssLocationProvider"
-
+// Define LOG_TAG and LOG_NDEBUG before <log/log.h> to overwrite the default values.
+#define LOG_TAG "GnssLocationProviderJni"
 #define LOG_NDEBUG 0
 
 #include <android/hardware/gnss/1.0/IGnss.h>
@@ -39,6 +39,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "gnss/GnssConfiguration.h"
 #include "hardware_legacy/power.h"
 #include "jni.h"
 #include "utils/Log.h"
@@ -59,7 +60,6 @@
 static jclass class_location;
 static jclass class_gnssNavigationMessage;
 static jclass class_gnssClock;
-static jclass class_gnssConfiguration_halInterfaceVersion;
 static jclass class_gnssAntennaInfoBuilder;
 static jclass class_phaseCenterOffset;
 static jclass class_sphericalCorrections;
@@ -125,7 +125,6 @@
 static jmethodID method_gnssNavigationMessageCtor;
 static jmethodID method_gnssClockCtor;
 static jmethodID method_gnssMeasurementCtor;
-static jmethodID method_halInterfaceVersionCtor;
 static jmethodID method_gnssAntennaInfoBuilderCtor;
 static jmethodID method_phaseCenterOffsetCtor;
 static jmethodID method_sphericalCorrectionsCtor;
@@ -149,6 +148,7 @@
 using android::String16;
 using android::wp;
 using android::binder::Status;
+using android::gnss::GnssConfigurationInterface;
 
 using android::hardware::Return;
 using android::hardware::Void;
@@ -156,7 +156,6 @@
 using android::hardware::hidl_string;
 using android::hardware::hidl_death_recipient;
 
-using android::hardware::gnss::PsdsType;
 using android::hardware::gnss::V1_0::GnssLocationFlags;
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -193,10 +192,6 @@
 using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
 using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
 using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
-using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
-using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
-using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
-using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
 using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
 using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
 using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
@@ -228,9 +223,13 @@
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
 
+using android::hardware::gnss::BlocklistedSource;
+using android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::PsdsType;
 using IGnssAidl = android::hardware::gnss::IGnss;
 using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
 using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
 
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
@@ -266,10 +265,6 @@
 sp<IGnssBatching_V2_0> gnssBatchingIface_V2_0 = nullptr;
 sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
 sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
-sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
-sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
-sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
-sp<IGnssConfiguration_V2_1> gnssConfigurationIface_V2_1 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -281,6 +276,8 @@
 sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
 sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr;
 
+std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
+
 #define WAKE_LOCK_NAME  "GPS"
 
 namespace android {
@@ -455,25 +452,14 @@
     }
 }
 
-static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
-                                const bool success) {
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage) {
     if (!status.isOk()) {
         ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
         return JNI_FALSE;
     }
-    if (!success) {
-        ALOGE("AIDL return failure: %s", errorMessage);
-        return JNI_FALSE;
-    }
     return JNI_TRUE;
 }
 
-static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
-    jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
-            method_halInterfaceVersionCtor, major, minor);
-    return version;
-}
-
 struct ScopedJniString {
     ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
         mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
@@ -2160,13 +2146,6 @@
     class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass);
     method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V");
 
-    jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass(
-            "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion");
-    class_gnssConfiguration_halInterfaceVersion =
-            (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
-    method_halInterfaceVersionCtor =
-            env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
-
     jclass arrayListClass = env->FindClass("java/util/ArrayList");
     class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
     method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
@@ -2174,6 +2153,8 @@
 
     jclass doubleArrayClass = env->FindClass("[D");
     class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
+
+    gnss::GnssConfiguration_class_init_once(env);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -2371,39 +2352,41 @@
         gnssNiIface = gnssNi;
     }
 
-    if (gnssHal_V2_1 != nullptr) {
+    if (gnssHalAidl != nullptr) {
+        sp<IGnssConfigurationAidl> gnssConfigurationAidl;
+        auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssConfiguration AIDL interface.")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
+        }
+    } else if (gnssHal_V2_1 != nullptr) {
         auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V2_1");
-        } else {
-            gnssConfigurationIface_V2_1 = gnssConfiguration;
-            gnssConfigurationIface_V2_0 = gnssConfigurationIface_V2_1;
-            gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_1;
-            gnssConfigurationIface = gnssConfigurationIface_V2_1;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_1")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
         }
     } else if (gnssHal_V2_0 != nullptr) {
         auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
-        } else {
-            gnssConfigurationIface_V2_0 = gnssConfiguration;
-            gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0;
-            gnssConfigurationIface = gnssConfigurationIface_V2_0;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_0")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
         }
     } else if (gnssHal_V1_1 != nullptr) {
         auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V1_1");
-        } else {
-            gnssConfigurationIface_V1_1 = gnssConfiguration;
-            gnssConfigurationIface = gnssConfigurationIface_V1_1;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_1")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V1_1>(gnssConfiguration);
         }
     } else {
-        auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
-        if (!gnssConfiguration_V1_0.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration");
-        } else {
-            gnssConfigurationIface = gnssConfiguration_V1_0;
+        auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_0")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V1_0>(gnssConfiguration);
         }
     }
 
@@ -2459,25 +2442,10 @@
 
 static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
         JNIEnv* env, jclass /* jclazz */) {
-    jint major, minor;
-    if (gnssConfigurationIface_V2_1 != nullptr) {
-        major = 2;
-        minor = 1;
-    }
-    else if (gnssConfigurationIface_V2_0 != nullptr) {
-        major = 2;
-        minor = 0;
-    } else if (gnssConfigurationIface_V1_1 != nullptr) {
-        major = 1;
-        minor = 1;
-    } else if (gnssConfigurationIface != nullptr) {
-        major = 1;
-        minor = 0;
-    } else {
+    if (gnssConfigurationIface == nullptr) {
         return nullptr;
     }
-
-    return createHalInterfaceVersionJavaObject(env, major, minor);
+    return gnssConfigurationIface->getVersion(env);
 }
 
 /* Initialization needed each time the GPS service is shutdown. */
@@ -2519,9 +2487,8 @@
     // Set IGnssPsds or IGnssXtra callback.
     if (gnssPsdsAidlIface != nullptr) {
         sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
-        bool success;
-        auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
-        if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+        auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl);
+        if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) {
             gnssPsdsAidlIface = nullptr;
         }
     } else {
@@ -2814,13 +2781,11 @@
 
     jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
     if (gnssPsdsAidlIface != nullptr) {
-        bool success;
         auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
                                                         std::vector<uint8_t>((const uint8_t*)bytes,
                                                                              (const uint8_t*)bytes +
-                                                                                     length),
-                                                        &success);
-        checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+                                                                                     length));
+        checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
     } else if (gnssPsdsIface != nullptr) {
         auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
                                                         std::string((const char*)bytes, length));
@@ -3486,9 +3451,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
-    return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
+    return gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
@@ -3498,25 +3461,17 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-    auto result = gnssConfigurationIface->setSuplVersion(version);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
+    return gnssConfigurationIface->setSuplVersion(version);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
                                                                jobject,
                                                                jint suplEs) {
-    if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
-        ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher.");
-        return JNI_FALSE;
-    }
-
     if (gnssConfigurationIface == nullptr) {
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setSuplEs(suplEs);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed.");
+    return gnssConfigurationIface->setSuplEs(suplEs);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
@@ -3526,26 +3481,17 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setSuplMode(mode);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
+    return gnssConfigurationIface->setSuplMode(mode);
 }
 
 static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
                                                                 jobject,
                                                                 jint gpsLock) {
-    if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
-        ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
-        return JNI_FALSE;
-    }
-
     if (gnssConfigurationIface == nullptr) {
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setGpsLock(gpsLock);
-    return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed.");
+    return gnssConfigurationIface->setGpsLock(gpsLock);
 }
 
 static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
@@ -3555,9 +3501,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setLppProfile(lppProfile);
-    return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
+    return gnssConfigurationIface->setLppProfile(lppProfile);
 }
 
 static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
@@ -3567,59 +3511,16 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
-    return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+    return gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
 }
 
 static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
         JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
-    if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) {
+    if (gnssConfigurationIface == nullptr) {
         ALOGI("IGnssConfiguration interface does not support satellite blacklist.");
         return JNI_FALSE;
     }
-
-    jint *constellation_array = env->GetIntArrayElements(constellations, 0);
-    if (nullptr == constellation_array) {
-        ALOGI("GetIntArrayElements returns nullptr.");
-        return JNI_FALSE;
-    }
-    jsize length = env->GetArrayLength(constellations);
-
-    jint *sv_id_array = env->GetIntArrayElements(sv_ids, 0);
-    if (nullptr == sv_id_array) {
-        ALOGI("GetIntArrayElements returns nullptr.");
-        return JNI_FALSE;
-    }
-
-    if (length != env->GetArrayLength(sv_ids)) {
-        ALOGI("Lengths of constellations and sv_ids are inconsistent.");
-        return JNI_FALSE;
-    }
-
-    if (gnssConfigurationIface_V2_1 != nullptr) {
-        hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources;
-        sources.resize(length);
-
-        for (int i = 0; i < length; i++) {
-            sources[i].constellation = static_cast<GnssConstellationType_V2_0>(constellation_array[i]);
-            sources[i].svid = sv_id_array[i];
-        }
-
-        auto result = gnssConfigurationIface_V2_1->setBlacklist_2_1(sources);
-        return checkHidlReturn(result, "IGnssConfiguration_V2_1 setBlacklist_2_1() failed.");
-    }
-
-    hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources;
-    sources.resize(length);
-
-    for (int i = 0; i < length; i++) {
-        sources[i].constellation = static_cast<GnssConstellationType_V1_0>(constellation_array[i]);
-        sources[i].svid = sv_id_array[i];
-    }
-
-    auto result = gnssConfigurationIface_V1_1->setBlacklist(sources);
-    return checkHidlReturn(result, "IGnssConfiguration setBlacklist() failed.");
+    return gnssConfigurationIface->setBlocklist(env, constellations, sv_ids);
 }
 
 static jboolean android_location_GnssConfiguration_set_es_extension_sec(
@@ -3628,15 +3529,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    if (gnssConfigurationIface_V2_0 == nullptr) {
-        ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
-                " versions earlier than 2.0.");
-        return JNI_FALSE;
-    }
-
-    auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
-    return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
+    return gnssConfigurationIface->setEsExtensionSec(emergencyExtensionSeconds);
 }
 
 static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp
new file mode 100644
index 0000000..8610c75
--- /dev/null
+++ b/services/core/jni/gnss/GnssConfiguration.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssConfigurationJni"
+
+#include "GnssConfiguration.h"
+
+using android::hardware::gnss::GnssConstellationType;
+using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
+
+using android::binder::Status;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+
+using android::hardware::gnss::IGnssConfiguration;
+using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
+using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
+using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
+using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
+
+using android::hardware::gnss::BlocklistedSource;
+using BlocklistedSource_V1_1 = IGnssConfiguration_V1_1::BlacklistedSource;
+using BlocklistedSource_V2_1 = IGnssConfiguration_V2_1::BlacklistedSource;
+
+namespace {
+
+jclass class_gnssConfiguration_halInterfaceVersion;
+jmethodID method_halInterfaceVersionCtor;
+
+jboolean checkAidlStatus(const Status& status, const char* errorMessage) {
+    if (!status.isOk()) {
+        ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+template <class T>
+inline void logHidlError(Return<T>& result, const char* errorMessage) {
+    ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str());
+}
+
+template <class T>
+jboolean checkHidlReturn(Return<T>& result, const char* errorMessage) {
+    if (!result.isOk()) {
+        logHidlError(result, errorMessage);
+        return JNI_FALSE;
+    } else {
+        return JNI_TRUE;
+    }
+}
+
+jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
+    return env->NewObject(class_gnssConfiguration_halInterfaceVersion,
+                          method_halInterfaceVersionCtor, major, minor);
+}
+
+} // anonymous namespace
+
+namespace android::gnss {
+
+void GnssConfiguration_class_init_once(JNIEnv* env) {
+    jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass(
+            "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion");
+    class_gnssConfiguration_halInterfaceVersion =
+            (jclass)env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
+    method_halInterfaceVersionCtor =
+            env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+}
+
+// Implementation of GnssConfiguration (AIDL HAL)
+
+GnssConfiguration::GnssConfiguration(const sp<IGnssConfiguration>& iGnssConfiguration)
+      : mIGnssConfiguration(iGnssConfiguration) {}
+
+jobject GnssConfiguration::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 3, 0);
+}
+
+jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) {
+    auto status = mIGnssConfiguration->setEmergencySuplPdn(enable);
+    return checkAidlStatus(status, "IGnssConfiguration setEmergencySuplPdn() failed.");
+}
+
+jboolean GnssConfiguration::setSuplVersion(jint version) {
+    auto status = mIGnssConfiguration->setSuplVersion(version);
+    return checkAidlStatus(status, "IGnssConfiguration setSuplVersion() failed.");
+}
+
+jboolean GnssConfiguration::setSuplEs(jint enable) {
+    ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration AIDL HAL.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration::setSuplMode(jint mode) {
+    auto status = mIGnssConfiguration->setSuplMode(mode);
+    return checkAidlStatus(status, "IGnssConfiguration setSuplMode() failed.");
+}
+
+jboolean GnssConfiguration::setGpsLock(jint gpsLock) {
+    ALOGI("Config parameter GPS_LOCK is not supported in IGnssConfiguration AIDL HAL.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration::setLppProfile(jint lppProfile) {
+    auto status = mIGnssConfiguration->setLppProfile(lppProfile);
+    return checkAidlStatus(status, "IGnssConfiguration setLppProfile() failed.");
+}
+
+jboolean GnssConfiguration::setGlonassPositioningProtocol(jint gnssPosProtocol) {
+    auto status = mIGnssConfiguration->setGlonassPositioningProtocol(gnssPosProtocol);
+    return checkAidlStatus(status, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+}
+
+jboolean GnssConfiguration::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    auto status = mIGnssConfiguration->setEsExtensionSec(emergencyExtensionSeconds);
+    return checkAidlStatus(status, "IGnssConfiguration setEsExtensionSec() failed.");
+}
+
+jboolean GnssConfiguration::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                         jintArray& sv_ids) {
+    auto sources =
+            getBlocklistedSources<BlocklistedSource, GnssConstellationType>(env, constellations,
+                                                                            sv_ids);
+    auto status = mIGnssConfiguration->setBlocklist(sources);
+    return checkAidlStatus(status, "IGnssConfiguration setBlocklist() failed.");
+}
+
+// Implementation of GnssConfiguration_V1_0
+
+GnssConfiguration_V1_0::GnssConfiguration_V1_0(
+        const sp<IGnssConfiguration_V1_0>& iGnssConfiguration)
+      : mIGnssConfiguration_V1_0(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V1_0::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 1, 0);
+}
+
+jboolean GnssConfiguration_V1_0::setEmergencySuplPdn(jint enable) {
+    auto result = mIGnssConfiguration_V1_0->setEmergencySuplPdn(enable);
+    return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplVersion(jint version) {
+    auto result = mIGnssConfiguration_V1_0->setSuplVersion(version);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplEs(jint enable) {
+    auto result = mIGnssConfiguration_V1_0->setSuplEs(enable);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplMode(jint mode) {
+    auto result = mIGnssConfiguration_V1_0->setSuplMode(mode);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setGpsLock(jint gpsLock) {
+    auto result = mIGnssConfiguration_V1_0->setGpsLock(gpsLock);
+    return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setLppProfile(jint lppProfile) {
+    auto result = mIGnssConfiguration_V1_0->setLppProfile(lppProfile);
+    return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setGlonassPositioningProtocol(jint gnssPosProtocol) {
+    auto result = mIGnssConfiguration_V1_0->setGlonassPositioningProtocol(gnssPosProtocol);
+    return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
+          " versions earlier than 2.0.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V1_0::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    ALOGI("IGnssConfiguration interface does not support satellite blocklist.");
+    return JNI_FALSE;
+}
+
+// Implementation of GnssConfiguration_V1_1
+
+GnssConfiguration_V1_1::GnssConfiguration_V1_1(
+        const sp<IGnssConfiguration_V1_1>& iGnssConfiguration)
+      : GnssConfiguration_V1_0{iGnssConfiguration}, mIGnssConfiguration_V1_1(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V1_1::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 1, 1);
+}
+
+jboolean GnssConfiguration_V1_1::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    auto sources = getBlocklistedSources<BlocklistedSource_V1_1,
+                                         GnssConstellationType_V1_0>(env, constellations, sv_ids);
+    auto result = mIGnssConfiguration_V1_1->setBlacklist(sources);
+    return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed.");
+}
+
+// Implementation of GnssConfiguration_V2_0
+
+GnssConfiguration_V2_0::GnssConfiguration_V2_0(
+        const sp<IGnssConfiguration_V2_0>& iGnssConfiguration)
+      : GnssConfiguration_V1_1{iGnssConfiguration}, mIGnssConfiguration_V2_0(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V2_0::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 2, 0);
+}
+
+jboolean GnssConfiguration_V2_0::setSuplEs(jint enable) {
+    ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and "
+          "higher.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V2_0::setGpsLock(jint enable) {
+    ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0 and "
+          "higher.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V2_0::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    auto result = mIGnssConfiguration_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
+    return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
+}
+
+// Implementation of GnssConfiguration_V2_1
+
+GnssConfiguration_V2_1::GnssConfiguration_V2_1(
+        const sp<IGnssConfiguration_V2_1>& iGnssConfiguration)
+      : GnssConfiguration_V2_0{iGnssConfiguration}, mIGnssConfiguration_V2_1(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V2_1::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 2, 1);
+}
+
+jboolean GnssConfiguration_V2_1::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    auto sources = getBlocklistedSources<BlocklistedSource_V2_1,
+                                         GnssConstellationType_V2_0>(env, constellations, sv_ids);
+    auto result = mIGnssConfiguration_V2_1->setBlacklist_2_1(sources);
+    return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssConfiguration.h b/services/core/jni/gnss/GnssConfiguration.h
new file mode 100644
index 0000000..ea0f178
--- /dev/null
+++ b/services/core/jni/gnss/GnssConfiguration.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
+#define _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssConfiguration.h>
+#include <android/hardware/gnss/1.1/IGnssConfiguration.h>
+#include <android/hardware/gnss/2.0/IGnssConfiguration.h>
+#include <android/hardware/gnss/2.1/IGnssConfiguration.h>
+#include <android/hardware/gnss/BnGnssConfiguration.h>
+#include <log/log.h>
+
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssConfiguration_class_init_once(JNIEnv* env);
+
+class GnssConfigurationInterface {
+public:
+    virtual ~GnssConfigurationInterface() {}
+    virtual jobject getVersion(JNIEnv* env) = 0;
+    virtual jboolean setEmergencySuplPdn(jint enable) = 0;
+    virtual jboolean setSuplVersion(jint version) = 0;
+    virtual jboolean setSuplEs(jint enable) = 0;
+    virtual jboolean setSuplMode(jint mode) = 0;
+    virtual jboolean setGpsLock(jint gpsLock) = 0;
+    virtual jboolean setLppProfile(jint lppProfile) = 0;
+    virtual jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) = 0;
+    virtual jboolean setEsExtensionSec(jint emergencyExtensionSeconds) = 0;
+    virtual jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) = 0;
+
+protected:
+    template <class T_BlocklistSource, class T_ConstellationType>
+    hardware::hidl_vec<T_BlocklistSource> getBlocklistedSources(JNIEnv* env,
+                                                                jintArray& constellations,
+                                                                jintArray& sv_ids) {
+        jint* constellation_array = env->GetIntArrayElements(constellations, 0);
+        if (nullptr == constellation_array) {
+            ALOGI("GetIntArrayElements returns nullptr.");
+            return JNI_FALSE;
+        }
+
+        jsize length = env->GetArrayLength(constellations);
+
+        jint* sv_id_array = env->GetIntArrayElements(sv_ids, 0);
+        if (nullptr == sv_id_array) {
+            ALOGI("GetIntArrayElements returns nullptr.");
+            return JNI_FALSE;
+        }
+
+        if (length != env->GetArrayLength(sv_ids)) {
+            ALOGI("Lengths of constellations and sv_ids are inconsistent.");
+            return JNI_FALSE;
+        }
+
+        hardware::hidl_vec<T_BlocklistSource> sources;
+        sources.resize(length);
+
+        for (int i = 0; i < length; i++) {
+            sources[i].constellation = static_cast<T_ConstellationType>(constellation_array[i]);
+            sources[i].svid = sv_id_array[i];
+        }
+
+        env->ReleaseIntArrayElements(constellations, constellation_array, 0);
+        env->ReleaseIntArrayElements(sv_ids, sv_id_array, 0);
+
+        return sources;
+    }
+};
+
+class GnssConfiguration : public GnssConfigurationInterface {
+public:
+    GnssConfiguration(const sp<android::hardware::gnss::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setEmergencySuplPdn(jint enable) override;
+    jboolean setSuplVersion(jint version) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setSuplMode(jint mode) override;
+    jboolean setGpsLock(jint gpsLock) override;
+    jboolean setLppProfile(jint lppProfile) override;
+    jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::IGnssConfiguration> mIGnssConfiguration;
+};
+
+class GnssConfiguration_V1_0 : public GnssConfigurationInterface {
+public:
+    GnssConfiguration_V1_0(
+            const sp<android::hardware::gnss::V1_0::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setEmergencySuplPdn(jint enable);
+    jboolean setSuplVersion(jint version) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setSuplMode(jint mode) override;
+    jboolean setGpsLock(jint gpsLock) override;
+    jboolean setLppProfile(jint lppProfile);
+    jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V1_0::IGnssConfiguration> mIGnssConfiguration_V1_0;
+};
+
+class GnssConfiguration_V1_1 : public GnssConfiguration_V1_0 {
+public:
+    GnssConfiguration_V1_1(
+            const sp<android::hardware::gnss::V1_1::IGnssConfiguration>& iGnssConfiguration);
+
+    jobject getVersion(JNIEnv* env) override;
+
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V1_1::IGnssConfiguration> mIGnssConfiguration_V1_1;
+};
+
+class GnssConfiguration_V2_0 : public GnssConfiguration_V1_1 {
+public:
+    GnssConfiguration_V2_0(
+            const sp<android::hardware::gnss::V2_0::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setGpsLock(jint enable) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+
+private:
+    const sp<android::hardware::gnss::V2_0::IGnssConfiguration> mIGnssConfiguration_V2_0;
+};
+
+class GnssConfiguration_V2_1 : public GnssConfiguration_V2_0 {
+public:
+    GnssConfiguration_V2_1(
+            const sp<android::hardware::gnss::V2_1::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V2_1::IGnssConfiguration> mIGnssConfiguration_V2_1;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index 5a4c682..9924708 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -29,6 +29,7 @@
                 <xs:attribute type="xs:boolean" name="disabled"/>
                 <xs:attribute type="xs:boolean" name="loggingOnly"/>
                 <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+                <xs:attribute type="xs:int" name="enableSinceTargetSdk"/>
                 <xs:attribute type="xs:string" name="description"/>
             </xs:extension>
         </xs:simpleContent>
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index 7def58d..e3640ed 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -6,6 +6,7 @@
     method public String getDescription();
     method public boolean getDisabled();
     method public int getEnableAfterTargetSdk();
+    method public int getEnableSinceTargetSdk();
     method public long getId();
     method public boolean getLoggingOnly();
     method public String getName();
@@ -13,6 +14,7 @@
     method public void setDescription(String);
     method public void setDisabled(boolean);
     method public void setEnableAfterTargetSdk(int);
+    method public void setEnableSinceTargetSdk(int);
     method public void setId(long);
     method public void setLoggingOnly(boolean);
     method public void setName(String);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 177b7b4..5dbb348 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4395,12 +4395,13 @@
         return mInjector.binderWithCleanCallingIdentity(() -> mUserManager.getUserInfo(userId));
     }
 
-    private boolean setPasswordPrivileged(@NonNull String password, int flags, int callingUid) {
+    private boolean setPasswordPrivileged(@NonNull String password, int flags,
+            CallerIdentity caller) {
         // Only allow setting password on an unsecured user
-        if (isLockScreenSecureUnchecked(UserHandle.getUserId(callingUid))) {
+        if (isLockScreenSecureUnchecked(caller.getUserId())) {
             throw new SecurityException("Cannot change current password");
         }
-        return resetPasswordInternal(password, 0, null, flags, callingUid);
+        return resetPasswordInternal(password, 0, null, flags, caller);
     }
 
     @Override
@@ -4410,19 +4411,22 @@
             return false;
         }
         if (password == null) password = "";
-        final int callingUid = mInjector.binderGetCallingUid();
-        final int userHandle = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity();
+        final int userHandle = caller.getUserId();
 
         // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to
         // set password to an unsecured user.
         if (hasCallingPermission(permission.RESET_PASSWORD)) {
-            return setPasswordPrivileged(password, flags, callingUid);
+            return setPasswordPrivileged(password, flags, caller);
         }
 
+        // If caller has PO (or DO) throw or fail silently depending on its target SDK level.
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller) || isProfileOwner(caller),
+                String.format("UID %d is not a device or profile owner", caller.getUid()));
+
         synchronized (getLockObject()) {
-            // If caller has PO (or DO) throw or fail silently depending on its target SDK level.
-            ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
-                    null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
+            ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
             if (admin != null) {
                 if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
                     Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
@@ -4444,7 +4448,8 @@
     }
 
     private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
-            int flags, int callingUid) {
+            int flags, CallerIdentity caller) {
+        final int callingUid = caller.getUid();
         final int userHandle = UserHandle.getUserId(callingUid);
         synchronized (getLockObject()) {
             final PasswordMetrics minMetrics = getPasswordMinimumMetrics(userHandle);
@@ -4473,7 +4478,7 @@
             return false;
         }
 
-        boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid);
+        boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller);
         boolean doNotAskCredentialsOnBoot =
                 (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
         if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -5388,14 +5393,14 @@
         // Retrieve the user ID of the calling process.
         final int userId = caller.getUserId();
         final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
+        // Ensure calling process is device/profile owner.
+        if (hasDoDelegation) {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        } else {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        }
+
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            if (hasDoDelegation) {
-                Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-            } else {
-                // TODO move whole condition out of synchronized block
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
             // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
             if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage,
                         getTargetSdk(who.getPackageName(), userId), scopes)) {
@@ -5516,11 +5521,10 @@
         }
 
         // Retrieve the user ID of the calling process.
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            return getDelegatePackagesInternalLocked(scope, userId);
+            return getDelegatePackagesInternalLocked(scope, caller.getUserId());
         }
     }
 
@@ -5649,11 +5653,12 @@
             String delegatePackage, String scope) {
         Objects.requireNonNull(who, "ComponentName is null");
 
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        // Ensure calling process is device/profile owner.
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            final DevicePolicyData policy = getUserData(userId);
+            final DevicePolicyData policy = getUserData(caller.getUserId());
 
             if (delegatePackage != null) {
                 // Set package as a delegate for scope if it is not already one.
@@ -5866,7 +5871,7 @@
                             + "organization-owned device.");
         }
         if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
-            Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller)
                             || calledByProfileOwnerOnOrgOwnedDevice,
                     "Only device owners or profile owners of organization-owned device can set "
                             + "WIPE_RESET_PROTECTION_DATA");
@@ -6625,8 +6630,8 @@
         }
 
         synchronized (getLockObject()) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            ActiveAdmin ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller),
+                    parent);
             if (ap.disableScreenCapture != disabled) {
                 ap.disableScreenCapture = disabled;
                 saveSettingsLocked(caller.getUserId());
@@ -7309,9 +7314,15 @@
 
     private boolean isDeviceOwner(CallerIdentity caller) {
         synchronized (getLockObject()) {
-            return mOwners.hasDeviceOwner()
-                    && mOwners.getDeviceOwnerUserId() == caller.getUserId()
-                    && mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+            if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
+                return false;
+            }
+
+            if (caller.hasAdminComponent()) {
+                return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+            } else {
+                return isUidDeviceOwnerLocked(caller.getUid());
+            }
         }
     }
 
@@ -7341,8 +7352,43 @@
      * @return true if {@code identity} is a profile owner, false otherwise.
      */
     public boolean isProfileOwner(CallerIdentity caller) {
-        final ComponentName profileOwner = getProfileOwner(caller.getUserId());
-        return profileOwner != null && profileOwner.equals(caller.getComponentName());
+        synchronized (getLockObject()) {
+            final ComponentName profileOwner = getProfileOwner(caller.getUserId());
+            // No profile owner.
+            if (profileOwner == null) {
+                return false;
+            }
+            // The admin ComponentName was specified, check it directly.
+            if (caller.hasAdminComponent()) {
+                return profileOwner.equals(caller.getComponentName());
+            } else {
+                return isUidProfileOwnerLocked(caller.getUid());
+            }
+        }
+    }
+
+    /**
+     * Checks if the app uid provided is the profile owner. This method should only be called
+     * if no componentName is available.
+     *
+     * @param appUid UID of the caller.
+     * @return true if the caller is the profile owner
+     */
+    private boolean isUidProfileOwnerLocked(int appUid) {
+        ensureLocked();
+
+        final int userId = UserHandle.getUserId(appUid);
+        final ComponentName profileOwnerComponent = mOwners.getProfileOwnerComponent(userId);
+        if (profileOwnerComponent == null) {
+            return false;
+        }
+        for (ActiveAdmin admin : getUserData(userId).mAdminList) {
+            final ComponentName currentAdminComponent = admin.info.getComponent();
+            if (admin.getUid() == appUid && profileOwnerComponent.equals(currentAdminComponent)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean hasProfileOwner(int userId) {
@@ -7652,8 +7698,7 @@
         enforceUserUnlocked(userId);
         synchronized (getLockObject()) {
             // Check if this is the profile owner who is calling
-            final ActiveAdmin admin =
-                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
 
             mInjector.binderWithCleanCallingIdentity(() -> {
                 clearProfileOwnerLocked(admin, userId);
@@ -8368,8 +8413,10 @@
     }
 
     private void enforceCanCallLockTaskLocked(ComponentName who) {
-        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        final int userId =  mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        final int userId =  caller.getUserId();
         if (!canUserUseLockTaskLocked(userId)) {
             throw new SecurityException("User " + userId + " is not allowed to use lock task");
         }
@@ -8533,10 +8580,11 @@
     public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
             ComponentName activity) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userHandle = UserHandle.getCallingUserId();
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        final int userHandle = caller.getUserId();
+        synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
@@ -8559,10 +8607,11 @@
     @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userHandle = UserHandle.getCallingUserId();
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        final int userHandle = caller.getUserId();
+        synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
@@ -8666,7 +8715,7 @@
 
         synchronized (getLockObject()) {
             final String componentName = agent.flattenToString();
-            if (caller.hasAdminComponent()) {
+            if (admin != null) {
                 final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle, parent);
                 if (ap == null) return null;
                 TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
@@ -8714,10 +8763,11 @@
     @Override
     public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
         Objects.requireNonNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            int userHandle = UserHandle.getCallingUserId();
+        synchronized (getLockObject()) {
+            int userHandle = caller.getUserId();
             DevicePolicyData userData = getUserData(userHandle);
             userData.mRestrictionsProvider = permissionProvider;
             saveSettingsLocked(userHandle);
@@ -8736,10 +8786,10 @@
     @Override
     public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
             long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -8785,9 +8835,11 @@
     @Override
     public void clearCrossProfileIntentFilters(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -9462,10 +9514,11 @@
     @Override
     public int logoutUser(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        final int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (!isUserAffiliatedWithDeviceLocked(callingUserId)) {
                 throw new SecurityException("Admin " + who +
                         " is neither the device owner or affiliated user's profile owner.");
@@ -9627,9 +9680,8 @@
 
         int userHandle = caller.getUserId();
         synchronized (getLockObject()) {
-            final ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(who,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+                    getProfileOwnerOrDeviceOwnerLocked(caller), parent);
 
             if (isDeviceOwner(caller)) {
                 if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
@@ -10546,19 +10598,17 @@
     public void setSystemSetting(ComponentName who, String setting, String value) {
         Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
             if (!SYSTEM_SETTINGS_ALLOWLIST.contains(setting)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
             }
 
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
-
             mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.settingsSystemPutStringForUser(setting, value, callingUserId));
+                    mInjector.settingsSystemPutStringForUser(setting, value, caller.getUserId()));
         }
     }
 
@@ -10704,11 +10754,11 @@
     @Override
     public void setSecureSetting(ComponentName who, String setting, String value) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
             if (isDeviceOwner(who, callingUserId)) {
                 if (!SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.contains(setting)
                         && !isCurrentUserDemo()) {
@@ -10800,8 +10850,10 @@
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
@@ -10814,9 +10866,10 @@
     @Override
     public boolean isMasterVolumeMuted(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        synchronized (getLockObject()) {
             AudioManager audioManager =
                     (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
             return audioManager.isMasterMute();
@@ -10825,13 +10878,13 @@
 
     @Override
     public void setUserIcon(ComponentName who, Bitmap icon) {
-        synchronized (getLockObject()) {
-            Objects.requireNonNull(who, "ComponentName is null");
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            int userId = UserHandle.getCallingUserId();
+        synchronized (getLockObject()) {
             mInjector.binderWithCleanCallingIdentity(
-                    () -> mUserManagerInternal.setUserIcon(userId, icon));
+                    () -> mUserManagerInternal.setUserIcon(caller.getUserId(), icon));
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_USER_ICON)
@@ -10842,13 +10895,15 @@
     @Override
     public boolean setKeyguardDisabled(ComponentName who, boolean disabled) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        final int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                throw new SecurityException("Admin " + who +
-                        " is neither the device owner or affiliated user's profile owner.");
-            }
+            Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+                    String.format(
+                            "Admin %s is neither the device owner or affiliated user's profile "
+                                    + "owner.", who));
         }
         if (isManagedProfile(userId)) {
             throw new SecurityException("Managed profile cannot disable keyguard");
@@ -10881,13 +10936,14 @@
 
     @Override
     public boolean setStatusBarDisabled(ComponentName who, boolean disabled) {
-        int userId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                throw new SecurityException("Admin " + who +
-                        " is neither the device owner or affiliated user's profile owner.");
-            }
+            Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+                    "Admin " + who
+                            + " is neither the device owner or affiliated user's profile owner.");
             if (isManagedProfile(userId)) {
                 throw new SecurityException("Managed profile cannot disable status bar");
             }
@@ -11628,32 +11684,24 @@
     }
 
     /**
-     * Checks if the caller of the method is the device owner app.
-     *
-     * @param callerUid UID of the caller.
-     * @return true if the caller is the device owner app
+     * Checks if any of the packages associated with the UID of the app provided is that
+     * of the device owner.
+     * @param appUid UID of the app to check.
+     * @return {@code true} if any of the packages are the device owner, {@code false} otherwise.
      */
-    @VisibleForTesting
-    boolean isCallerDeviceOwner(int callerUid) {
-        synchronized (getLockObject()) {
-            if (!mOwners.hasDeviceOwner()) {
-                return false;
-            }
-            if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
-                return false;
-            }
-            final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
-                    .getPackageName();
-                try {
-                    String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid);
-                    for (String pkg : pkgs) {
-                        if (deviceOwnerPackageName.equals(pkg)) {
-                            return true;
-                        }
-                    }
-                } catch (RemoteException e) {
-                    return false;
+    private boolean isUidDeviceOwnerLocked(int appUid) {
+        ensureLocked();
+        final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
+                .getPackageName();
+        try {
+            String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(appUid);
+            for (String pkg : pkgs) {
+                if (deviceOwnerPackageName.equals(pkg)) {
+                    return true;
                 }
+            }
+        } catch (RemoteException e) {
+            return false;
         }
         return false;
     }
@@ -12633,9 +12681,11 @@
         }
 
         final Set<String> affiliationIds = new ArraySet<>(ids);
-        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+        final int callingUserId = caller.getUserId();
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             getUserData(callingUserId).mAffiliationIds = affiliationIds;
             saveSettingsLocked(callingUserId);
             if (callingUserId != UserHandle.USER_SYSTEM && isDeviceOwner(admin, callingUserId)) {
@@ -12661,10 +12711,11 @@
         }
 
         Objects.requireNonNull(admin);
+        final CallerIdentity caller = getCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            return new ArrayList<String>(
-                    getUserData(mInjector.userHandleGetCallingUserId()).mAffiliationIds);
+            return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds);
         }
     }
 
@@ -13174,11 +13225,11 @@
             return Collections.emptyList();
         }
         Objects.requireNonNull(admin);
+        final CallerIdentity caller = getCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
+            final int callingUserId = caller.getUserId();
             return mInjector.binderWithCleanCallingIdentity(() -> {
                 ArrayList<UserHandle> targetUsers = new ArrayList<>();
                 if (!isDeviceOwner(admin, callingUserId)) {
@@ -13581,9 +13632,11 @@
         if (token == null || token.length < 32) {
             throw new IllegalArgumentException("token must be at least 32-byte long");
         }
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int userHandle = caller.getUserId();
 
             DevicePolicyData policy = getUserData(userHandle);
             return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -13603,9 +13656,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int userHandle = caller.getUserId();
 
             DevicePolicyData policy = getUserData(userHandle);
             if (policy.mPasswordTokenHandle != 0) {
@@ -13626,11 +13681,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            return isResetPasswordTokenActiveForUserLocked(userHandle);
+        synchronized (getLockObject()) {
+            return isResetPasswordTokenActiveForUserLocked(caller.getUserId());
         }
     }
 
@@ -13650,15 +13705,16 @@
             return false;
         }
         Objects.requireNonNull(token);
-        synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            DevicePolicyData policy = getUserData(userHandle);
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        synchronized (getLockObject()) {
+            DevicePolicyData policy = getUserData(caller.getUserId());
             if (policy.mPasswordTokenHandle != 0) {
                 final String password = passwordOrNull != null ? passwordOrNull : "";
                 return resetPasswordInternal(password, policy.mPasswordTokenHandle, token,
-                        flags, mInjector.binderGetCallingUid());
+                        flags, caller);
             } else {
                 Slog.w(LOG_TAG, "No saved token handle");
             }
@@ -13973,9 +14029,11 @@
     @Override
     @Nullable
     public PersistableBundle getTransferOwnershipBundle() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int callingUserId = caller.getUserId();
             final File bundleFile = new File(
                     mInjector.environmentGetUserSystemDirectory(callingUserId),
                     TRANSFER_OWNERSHIP_PARAMETERS_XML);
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 91cb481..7c3b7a6 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -122,7 +122,7 @@
 
         @Override
         public ParceledListSlice<ConversationChannel> getRecentConversations() {
-            enforceSystemOrRoot("get recent conversations");
+            enforceSystemRootOrSystemUI(getContext(), "get recent conversations");
             return new ParceledListSlice<>(
                     mDataManager.getRecentConversations(
                             Binder.getCallingUserHandle().getIdentifier()));
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 9d2393b..0ab1501 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -83,6 +83,8 @@
 
     private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>();
 
+    private Injector mInjector;
+
     @Before
     public void setUp() throws Exception {
         mMockitoSession = mockitoSession()
@@ -94,8 +96,9 @@
         doReturn(mMockedResources).when(mMockedContext).getResources();
         LocalServices.removeServiceForTest(LightsManager.class);
         LocalServices.addService(LightsManager.class, mMockedLightsManager);
+        mInjector = new Injector();
         mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
-                mListener);
+                mListener, mInjector);
         spyOn(mAdapter);
         doReturn(mMockedContext).when(mAdapter).getOverlayContext();
     }
@@ -222,7 +225,7 @@
         display.configs = configs;
         display.activeConfig = 1;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
@@ -272,7 +275,7 @@
                 new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0);
         display.hdrCapabilities = changedHdrCapabilities;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
@@ -308,7 +311,7 @@
         final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT};
         display.colorModes = changedColorModes;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
@@ -322,6 +325,35 @@
         assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes);
     }
 
+    @Test
+    public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
+        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
+                createFakeDisplayConfig(1920, 1080, 60f),
+                createFakeDisplayConfig(1920, 1080, 50f)
+        };
+        final int activeConfig = 0;
+        FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
+        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // Change the display
+        display.configs = new SurfaceControl.DisplayConfig[]{
+                createFakeDisplayConfig(1920, 1080, 60f)
+        };
+        // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+        // trigger ArrayOutOfBoundsException.
+        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+    }
+
     private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
                                   float expectedXdpi,
                                   float expectedYDpi,
@@ -356,6 +388,8 @@
         public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
         public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
                 1000, 1000, 0);
+        public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
+                new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f);
 
         private FakeDisplay(int port) {
             this.address = createDisplayAddress(port);
@@ -387,7 +421,7 @@
                 () -> SurfaceControl.getDisplayColorModes(display.token));
         doReturn(display.hdrCapabilities).when(
                 () -> SurfaceControl.getHdrCapabilities(display.token));
-        doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f))
+        doReturn(display.desiredDisplayConfigSpecs)
                 .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
     }
 
@@ -422,7 +456,7 @@
         return config;
     }
 
-    private void waitForHandlerToComplete(Handler handler, long waitTimeMs)
+    private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
             throws InterruptedException {
         final Object lock = new Object();
         synchronized (lock) {
@@ -435,6 +469,35 @@
         }
     }
 
+    private class HotplugTransmitter {
+        private final Handler mHandler;
+        private final LocalDisplayAdapter.DisplayEventListener mListener;
+
+        HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener) {
+            mHandler = new Handler(looper);
+            mListener = listener;
+        }
+
+        public void sendHotplug(FakeDisplay display, boolean connected)
+                throws InterruptedException {
+            mHandler.post(() -> mListener.onHotplug(/* timestampNanos = */ 0,
+                    display.address.getPhysicalDisplayId(), connected));
+            waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        }
+    }
+
+    private class Injector extends LocalDisplayAdapter.Injector {
+        private HotplugTransmitter mTransmitter;
+        @Override
+        public void setDisplayEventListenerLocked(Looper looper,
+                LocalDisplayAdapter.DisplayEventListener listener) {
+            mTransmitter = new HotplugTransmitter(looper, listener);
+        }
+        public HotplugTransmitter getTransmitter() {
+            return mTransmitter;
+        }
+    }
+
     private class TestListener implements DisplayAdapter.Listener {
         public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>();
         public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
index 968a402..3ace3f4 100644
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -27,8 +27,6 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -41,7 +39,7 @@
     private Context mContext;
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
     private BluetoothAdapter mBluetoothAdapter;
-    private AirplaneModeHelper mHelper;
+    private BluetoothModeChangeHelper mHelper;
 
     @Mock BluetoothManagerService mBluetoothManagerService;
 
@@ -49,7 +47,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
 
-        mHelper = mock(AirplaneModeHelper.class);
+        mHelper = mock(BluetoothModeChangeHelper.class);
         when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
                 .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
         doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 9a2ce3c..d2d85c8 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -486,7 +486,7 @@
             outLaunched.value = false;
             intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                     outLaunched);
-            assertFalse(intercepted);
+            assertTrue(intercepted);
             assertFalse(outLaunched.value);
         }
 
@@ -538,7 +538,7 @@
             outLaunched.value = false;
             intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                     outLaunched);
-            assertFalse(intercepted);
+            assertTrue(intercepted);
             assertFalse(outLaunched.value);
         }
 
@@ -564,6 +564,60 @@
     }
 
     @Test
+    public void
+            testInterceptPowerKeyDown_tenInboundPresses_panicGestureEnabled_pressesIntercepted() {
+        withPanicGestureEnabledSettingValue(true);
+        mGestureLauncherService.updatePanicButtonGestureEnabled();
+        withUserSetupCompleteValue(true);
+
+        // First button press does nothing
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        // 3 more button presses which should not trigger any gesture, but intercepts action.
+        for (int i = 0; i < 3; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+
+        // Fifth button press should trigger the panic flow
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertTrue(outLaunched.value);
+        assertTrue(intercepted);
+
+        // 5 more button presses which should not trigger any gesture, but intercepts action.
+        for (int i = 0; i < 5; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+    }
+
+    @Test
     public void testInterceptPowerKeyDown_longpress() {
         withCameraDoubleTapPowerEnableConfigValue(true);
         withCameraDoubleTapPowerDisableSettingValue(0);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index faf7169a..241b5a9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -68,7 +68,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.util.SparseArray;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
@@ -834,12 +833,13 @@
             // Reset the current state
             mHandler.reset();
             clearPendingUidChanges();
+            uidRecord.pendingChange.isPending = false;
 
             mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
 
             // Verify that pendingChange is updated correctly.
-            final ChangeRecord pendingChange = getPendingChange(uidRecord.uid);
-            assertNotNull(pendingChange);
+            final ChangeRecord pendingChange = uidRecord.pendingChange;
+            assertTrue(pendingChange.isPending);
             assertEquals(TEST_UID, pendingChange.uid);
             assertEquals(expectedProcState, pendingChange.procState);
             assertEquals(TEST_PROC_STATE_SEQ1, pendingChange.procStateSeq);
@@ -923,22 +923,16 @@
         mAms.mProcessList.mActiveUids.clear();
     }
 
-    private ChangeRecord getPendingChange(int uid) {
-        final SparseArray<ChangeRecord> pendingChanges =
-                mAms.mUidObserverController.getPendingUidChangesForTest();
-        return pendingChanges.get(uid);
-    }
-
     private void addPendingUidChange(ChangeRecord record) {
-        mAms.mUidObserverController.getPendingUidChangesForTest().put(record.uid, record);
+        mAms.mUidObserverController.getPendingUidChangesForTest().add(record);
     }
 
     private void addPendingUidChanges(ArrayList<ChangeRecord> changes) {
-        final SparseArray<ChangeRecord> pendingChanges =
+        final ArrayList<ChangeRecord> pendingChanges =
                 mAms.mUidObserverController.getPendingUidChangesForTest();
         for (int i = 0; i < changes.size(); ++i) {
             final ChangeRecord record = changes.get(i);
-            pendingChanges.put(record.uid, record);
+            pendingChanges.add(record);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
index 57c581e..1de5f6f 100644
--- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -31,6 +31,7 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -51,6 +52,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
 
 @SmallTest
 public class UidObserverControllerTest {
@@ -66,36 +70,41 @@
 
     @Before
     public void setUp() {
-        mUidObserverController = new UidObserverController(Mockito.mock(Handler.class));
+        MockitoAnnotations.initMocks(this);
+        mUidObserverController = new UidObserverController(mock(Handler.class));
     }
 
     @Test
     public void testEnqueueUidChange() {
-        int change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_ACTIVE,
-                PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_CAPABILITY_ALL, 0, false);
+        int change = mUidObserverController.enqueueUidChange(null, TEST_UID1,
+                UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+                PROCESS_CAPABILITY_ALL, 0, false);
         assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
                 UidRecord.CHANGE_ACTIVE, change);
         assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
-                PROCESS_CAPABILITY_ALL, 0, false);
-        assertNull(getPendingChange(TEST_UID2));
+                PROCESS_CAPABILITY_ALL, 0, false, null);
+        final ChangeRecord record1 = getLatestPendingChange(TEST_UID1);
+        assertNull(getLatestPendingChange(TEST_UID2));
 
-        change = mUidObserverController.enqueueUidChange(TEST_UID2, UidRecord.CHANGE_CACHED,
-                PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, 99, true);
+        final ChangeRecord record2 = new ChangeRecord();
+        change = mUidObserverController.enqueueUidChange(record2, TEST_UID2,
+                UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE,
+                99, true);
         assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
                 UidRecord.CHANGE_CACHED, change);
         assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
-                PROCESS_CAPABILITY_ALL, 0, false);
+                PROCESS_CAPABILITY_ALL, 0, false, null);
         assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
-                PROCESS_CAPABILITY_NONE, 99, true);
+                PROCESS_CAPABILITY_NONE, 99, true, record2);
 
-        change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_UNCACHED,
-                PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+        change = mUidObserverController.enqueueUidChange(record1, TEST_UID1,
+                UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
         assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change),
                 UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change);
         assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
-                PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+                PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false, record1);
         assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
-                PROCESS_CAPABILITY_NONE, 99, true);
+                PROCESS_CAPABILITY_NONE, 99, true, record2);
     }
 
     @Test
@@ -135,11 +144,11 @@
         addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE,
                 PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false);
 
-        final IUidObserver observer1 = Mockito.mock(IUidObserver.Stub.class);
+        final IUidObserver observer1 = mock(IUidObserver.Stub.class);
         registerObserver(observer1,
                 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
                 PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
-        final IUidObserver observer2 = Mockito.mock(IUidObserver.Stub.class);
+        final IUidObserver observer2 = mock(IUidObserver.Stub.class);
         registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
                 PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
 
@@ -209,13 +218,16 @@
         record.procStateSeq = procStateSeq;
         record.capability = capability;
         record.ephemeral = ephemeral;
-        mUidObserverController.getPendingUidChangesForTest().put(uid, record);
+        mUidObserverController.getPendingUidChangesForTest().add(record);
     }
 
     private void assertPendingChange(int uid, int change, int procState, long procStateSeq,
-            int capability, boolean ephemeral) {
-        final ChangeRecord record = getPendingChange(uid);
+            int capability, boolean ephemeral, ChangeRecord expectedRecord) {
+        final ChangeRecord record = getLatestPendingChange(uid);
         assertNotNull(record);
+        if (expectedRecord != null) {
+            assertEquals(expectedRecord, record);
+        }
         assertEquals(change, record.change);
         assertEquals(procState, record.procState);
         assertEquals(procStateSeq, record.procStateSeq);
@@ -223,8 +235,16 @@
         assertEquals(ephemeral, record.ephemeral);
     }
 
-    private ChangeRecord getPendingChange(int uid) {
-        return mUidObserverController.getPendingUidChangesForTest().get(uid);
+    private ChangeRecord getLatestPendingChange(int uid) {
+        final ArrayList<ChangeRecord> changeRecords = mUidObserverController
+                .getPendingUidChangesForTest();
+        for (int i = changeRecords.size() - 1; i >= 0; --i) {
+            final ChangeRecord record = changeRecords.get(i);
+            if (record.uid == uid) {
+                return record;
+            }
+        }
+        return null;
     }
 
     private static String changeToStr(int change) {
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
index 24f7830..081bfb5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
@@ -50,8 +50,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mAppSearchImpl = new AppSearchImpl(mTemporaryFolder.newFolder());
-        mAppSearchImpl.initialize();
+        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
     }
 
     /**
@@ -288,7 +287,8 @@
         SchemaProto finalSchemaProto = schemaProto;
         AppSearchException e = expectThrows(AppSearchException.class, () ->
                 mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/false));
-        assertThat(e).hasMessageThat().isEqualTo("Schema is incompatible.");
+        assertThat(e).hasMessageThat().contains("Schema is incompatible");
+        assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
 
         // ForceOverride to delete.
         mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/true);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java
new file mode 100644
index 0000000..a95290d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 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.appsearch.external.localbackend.converter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.GenericDocument;
+
+import com.android.server.appsearch.proto.DocumentProto;
+import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.protobuf.ByteString;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public class GenericDocumentToProtoConverterTest {
+    private static final byte[] BYTE_ARRAY_1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
+    private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
+    private static final GenericDocument DOCUMENT_PROPERTIES_1 =
+            new GenericDocument.Builder<GenericDocument.Builder<?>>(
+                    "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+            .setCreationTimestampMillis(12345L)
+            .build();
+    private static final GenericDocument DOCUMENT_PROPERTIES_2 =
+            new GenericDocument.Builder<GenericDocument.Builder<?>>(
+                    "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+            .setCreationTimestampMillis(6789L)
+            .build();
+
+    @Test
+    public void testDocumentProtoConvert() {
+        GenericDocument document =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setScore(1)
+                        .setTtlMillis(1L)
+                        .setNamespace("namespace")
+                        .setProperty("longKey1", 1L)
+                        .setProperty("doubleKey1", 1.0)
+                        .setProperty("booleanKey1", true)
+                        .setProperty("stringKey1", "test-value1")
+                        .setProperty("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2)
+                        .setProperty("documentKey1", DOCUMENT_PROPERTIES_1)
+                        .setProperty(GenericDocument.PROPERTIES_FIELD, DOCUMENT_PROPERTIES_2)
+                        .build();
+
+        // Create the Document proto. Need to sort the property order by key.
+        DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
+                .setUri("uri1")
+                .setSchema("schemaType1")
+                .setCreationTimestampMs(5L)
+                .setScore(1)
+                .setTtlMs(1L)
+                .setNamespace("namespace");
+        HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
+        propertyProtoMap.put("longKey1",
+                PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
+        propertyProtoMap.put("doubleKey1",
+                PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
+        propertyProtoMap.put("booleanKey1",
+                PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
+        propertyProtoMap.put("stringKey1",
+                PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
+        propertyProtoMap.put("byteKey1",
+                PropertyProto.newBuilder().setName("byteKey1")
+                        .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1))
+                        .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2)));
+        propertyProtoMap.put("documentKey1",
+                PropertyProto.newBuilder().setName("documentKey1")
+                        .addDocumentValues(
+                                GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1)));
+        propertyProtoMap.put(GenericDocument.PROPERTIES_FIELD,
+                PropertyProto.newBuilder().setName(GenericDocument.PROPERTIES_FIELD)
+                        .addDocumentValues(
+                                GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2)));
+        List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
+        Collections.sort(sortedKey);
+        for (String key : sortedKey) {
+            documentProtoBuilder.addProperties(propertyProtoMap.get(key));
+        }
+        DocumentProto documentProto = documentProtoBuilder.build();
+        assertThat(GenericDocumentToProtoConverter.convert(document))
+                .isEqualTo(documentProto);
+        assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
similarity index 75%
rename from core/tests/coretests/src/android/app/appsearch/SnippetTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
index 95f5b10..e9357aa 100644
--- a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,24 +14,23 @@
  * limitations under the License.
  */
 
-package android.app.appsearch;
+package com.android.server.appsearch.external.localbackend.converter;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.PropertyProto;
-import android.app.appsearch.proto.SearchResultProto;
-import android.app.appsearch.proto.SnippetMatchProto;
-import android.app.appsearch.proto.SnippetProto;
+import android.app.appsearch.SearchResult;
 
-import androidx.test.filters.SmallTest;
+import com.android.server.appsearch.proto.DocumentProto;
+import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.proto.SearchResultProto;
+import com.android.server.appsearch.proto.SnippetMatchProto;
+import com.android.server.appsearch.proto.SnippetProto;
 
 import org.junit.Test;
 
-@SmallTest
 public class SnippetTest {
 
-    // TODO(sidchhabra): Add tests for Double and Long Snippets.
+    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
     public void testSingleStringSnippet() {
 
@@ -74,22 +73,26 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
         // Making ResultReader and getting Snippet values.
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
-            MatchInfo match = result.getMatchInfo().get(0);
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
+            SearchResult.MatchInfo match = result.getMatches().get(0);
             assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
             assertThat(match.getFullText()).isEqualTo(propertyValueString);
             assertThat(match.getExactMatch()).isEqualTo(exactMatch);
+            assertThat(match.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/29, /*upper=*/32));
+            assertThat(match.getFullText()).isEqualTo(propertyValueString);
+            assertThat(match.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/26, /*upper=*/32));
             assertThat(match.getSnippet()).isEqualTo(window);
         }
     }
 
-    // TODO(sidchhabra): Add tests for Double and Long Snippets.
+    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
-    public void testNoSnippets() {
+    public void testNoSnippets() throws Exception {
 
         final String propertyKeyString = "content";
         final String propertyValueString = "A commonly used fake word is foo.\n"
@@ -117,16 +120,15 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
-            assertThat(result.getMatchInfo()).isEqualTo(null);
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
+            assertThat(result.getMatches()).isEqualTo(null);
         }
     }
 
     @Test
-    public void testMultipleStringSnippet() {
+    public void testMultipleStringSnippet() throws Exception {
         final String searchWord = "Test";
 
         // Building the SearchResult received from query.
@@ -178,22 +180,29 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
         // Making ResultReader and getting Snippet values.
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
 
-            MatchInfo match1 = result.getMatchInfo().get(0);
+            SearchResult.MatchInfo match1 = result.getMatches().get(0);
             assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
             assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
+            assertThat(match1.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/4));
             assertThat(match1.getExactMatch()).isEqualTo("Test");
+            assertThat(match1.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/9));
             assertThat(match1.getSnippet()).isEqualTo("Test Name");
 
-            MatchInfo match2 = result.getMatchInfo().get(1);
+            SearchResult.MatchInfo match2 = result.getMatches().get(1);
             assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
             assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
+            assertThat(match2.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
             assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
+            assertThat(match2.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
             assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 2cbe7be..870fe4a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -39,58 +39,79 @@
         return new CompatConfigBuilder(buildClassifier, context);
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, false, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, true, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) {
+        mChanges.add(new CompatChange(id, name, sdk, -1, false, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) {
-        mChanges.add(new CompatChange(id, name, sdk, false, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, -1, true, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id,
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id,
             String description) {
-        mChanges.add(new CompatChange(id, "", sdk, false, false, description));
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) {
+        mChanges.add(new CompatChange(id, name, -1, sdk, false, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, true, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id,
+            String description) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, false, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, ""));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, false, false, ""));
         return this;
     }
     CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, false, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, description));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, true, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, ""));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, true, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, true, false, ""));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, true, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, description));
         return this;
     }
 
     CompatConfigBuilder addLoggingOnlyChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, false, true, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, true, ""));
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8be9213..e588370 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -98,7 +98,7 @@
     @Test
     public void testTargetSdkChangeDisabled() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithId(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -109,7 +109,7 @@
     @Test
     public void testTargetSdkChangeEnabled() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithId(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -119,7 +119,7 @@
     @Test
     public void testDisabledOverrideTargetSdkChange() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkDisabledChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithIdDefaultDisabled(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -293,8 +293,8 @@
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithId(2L)
-                .addTargetSdkChangeWithId(3, 3L)
-                .addTargetSdkChangeWithId(4, 4L)
+                .addEnableSinceSdkChangeWithId(3, 3L)
+                .addEnableSinceSdkChangeWithId(4, 4L)
                 .build();
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
@@ -314,8 +314,8 @@
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithId(2L)
-                .addTargetSdkChangeWithId(3, 3L)
-                .addTargetSdkChangeWithId(4, 4L)
+                .addEnableSinceSdkChangeWithId(3, 3L)
+                .addEnableSinceSdkChangeWithId(4, 4L)
                 .build();
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index d45589d..c53b29a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -87,9 +87,9 @@
     public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
-                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                     .addEnabledChangeWithId(4)
                     .addDisabledChangeWithId(5)
                     .addLoggingOnlyChangeWithId(6).build();
@@ -131,9 +131,9 @@
     public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
-                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                     .addEnabledChangeWithId(4)
                     .addDisabledChangeWithId(5)
                     .addLoggingOnlyChangeWithId(6).build();
@@ -174,9 +174,9 @@
     public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addDisabledChangeWithId(4).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
@@ -245,9 +245,9 @@
     public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addEnabledChangeWithId(4)
                         .addDisabledChangeWithId(5)
                         .addLoggingOnlyChangeWithId(6).build();
@@ -288,8 +288,8 @@
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -313,7 +313,7 @@
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -371,9 +371,9 @@
     public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addEnabledChangeWithId(4)
                         .addDisabledChangeWithId(5)
                         .addLoggingOnlyChangeWithId(6).build();
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index cef02ff..64014ba 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -84,22 +84,22 @@
         mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithIdAndName(2L, "change2")
-                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
-                new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
-                        "description"),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
-                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
-                new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+                new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false,
+                        "desc"),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""),
+                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""),
+                new CompatibilityChangeInfo(7L, "", -1, -1, false, true, ""));
     }
 
     @Test
@@ -107,18 +107,18 @@
         mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithIdAndName(2L, "change2")
-                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 631b4d4..30b1b3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5728,6 +5728,7 @@
         // Device owner should be allowed to request Device ID attestation.
         dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1));
 
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
         // Another package must not be allowed to request Device ID attestation.
         assertExpectException(SecurityException.class, null,
                 () -> dpms.enforceCallerCanRequestDeviceIdAttestation(
@@ -5757,6 +5758,7 @@
         dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1));
 
         // But not another package.
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
         assertExpectException(SecurityException.class, null,
                 () -> dpms.enforceCallerCanRequestDeviceIdAttestation(
                         dpms.getCallerIdentity(null, admin2.getPackageName())));
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index e281f2b..391611b 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -35,8 +35,8 @@
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
 
-    private static final String OVERLAY = "com.dummy.overlay";
-    private static final String TARGET = "com.dummy.target";
+    private static final String OVERLAY = "com.test.overlay";
+    private static final String TARGET = "com.test.target";
     private static final int USER = 0;
 
     private static final String OVERLAY2 = OVERLAY + "2";
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index c1d862a..4f882ce 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -39,8 +39,8 @@
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
 
-    private static final String OVERLAY = "com.dummy.overlay";
-    private static final String TARGET = "com.dummy.target";
+    private static final String OVERLAY = "com.test.overlay";
+    private static final String TARGET = "com.test.target";
     private static final int USER = 0;
 
     private static final String OVERLAY2 = OVERLAY + "2";
@@ -50,7 +50,7 @@
     private static final String OVERLAY3 = OVERLAY + "3";
     private static final int USER3 = USER2 + 1;
 
-    private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.dummy.ref";
+    private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref";
     private static final String CERT_CONFIG_OK = "config_certificate_ok";
     private static final String CERT_CONFIG_NOK = "config_certificate_nok";
 
@@ -149,7 +149,7 @@
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertState(STATE_MISSING_TARGET, OVERLAY, USER);
 
-        final DummyDeviceState.PackageBuilder target = target(TARGET);
+        final FakeDeviceState.PackageBuilder target = target(TARGET);
         installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
@@ -169,9 +169,9 @@
 
     @Test
     public void testOnOverlayPackageUpgraded() {
-        final DummyListener listener = getListener();
-        final DummyDeviceState.PackageBuilder target = target(TARGET);
-        final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
+        final FakeListener listener = getListener();
+        final FakeDeviceState.PackageBuilder target = target(TARGET);
+        final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
         installNewPackage(target, USER);
         installNewPackage(overlay, USER);
         listener.count = 0;
@@ -181,7 +181,7 @@
         // upgrade to a version where the overlay has changed its target
         // expect once for the old target package, once for the new target package
         listener.count = 0;
-        final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
+        final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
         upgradePackage(overlay2, USER);
         assertEquals(3, listener.count);
 
@@ -193,7 +193,7 @@
     @Test
     public void testListener() {
         final OverlayManagerServiceImpl impl = getImpl();
-        final DummyListener listener = getListener();
+        final FakeListener listener = getListener();
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertEquals(1, listener.count);
         listener.count = 0;
@@ -219,12 +219,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == CONFIG_SIGNATURE);
     }
 
@@ -237,12 +237,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -252,12 +252,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -266,12 +266,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -284,12 +284,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 2faf29f..006dda0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -48,19 +48,19 @@
 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
 class OverlayManagerServiceImplTestsBase {
     private OverlayManagerServiceImpl mImpl;
-    private DummyDeviceState mState;
-    private DummyListener mListener;
-    private DummyPackageManagerHelper mPackageManager;
-    private DummyIdmapDaemon mIdmapDaemon;
+    private FakeDeviceState mState;
+    private FakeListener mListener;
+    private FakePackageManagerHelper mPackageManager;
+    private FakeIdmapDaemon mIdmapDaemon;
     private OverlayConfig mOverlayConfig;
     private String mConfigSignaturePackageName;
 
     @Before
     public void setUp() {
-        mState = new DummyDeviceState();
-        mListener = new DummyListener();
-        mPackageManager = new DummyPackageManagerHelper(mState);
-        mIdmapDaemon = new DummyIdmapDaemon(mState);
+        mState = new FakeDeviceState();
+        mListener = new FakeListener();
+        mPackageManager = new FakePackageManagerHelper(mState);
+        mIdmapDaemon = new FakeIdmapDaemon(mState);
         mOverlayConfig = mock(OverlayConfig.class);
         when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY);
         when(mOverlayConfig.isEnabled(any())).thenReturn(false);
@@ -81,15 +81,15 @@
         return mImpl;
     }
 
-    DummyListener getListener() {
+    FakeListener getListener() {
         return mListener;
     }
 
-    DummyIdmapDaemon getIdmapd() {
+    FakeIdmapDaemon getIdmapd() {
         return mIdmapDaemon;
     }
 
-    DummyDeviceState getState() {
+    FakeDeviceState getState() {
         return mState;
     }
 
@@ -116,27 +116,27 @@
         assertEquals(expected, actual);
     }
 
-    DummyDeviceState.PackageBuilder app(String packageName) {
-        return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
+    FakeDeviceState.PackageBuilder app(String packageName) {
+        return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
                 null /* targetOverlayableName */, "data");
     }
 
-    DummyDeviceState.PackageBuilder target(String packageName) {
-        return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
+    FakeDeviceState.PackageBuilder target(String packageName) {
+        return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
                 null /* targetOverlayableName */, "");
     }
 
-    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
+    FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
         return overlay(packageName, targetPackageName, null /* targetOverlayableName */);
     }
 
-    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
+    FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
             String targetOverlayableName) {
-        return new DummyDeviceState.PackageBuilder(packageName, targetPackageName,
+        return new FakeDeviceState.PackageBuilder(packageName, targetPackageName,
                 targetOverlayableName, "");
     }
 
-    void addPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+    void addPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         mState.add(pkg, userId);
     }
 
@@ -155,7 +155,7 @@
      *
      * @throws IllegalStateException if the package is currently installed
      */
-    void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         if (mState.select(pkg.packageName, userId) != null) {
             throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
@@ -178,8 +178,8 @@
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) {
-        final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
+    void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+        final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
         if (replacedPackage == null) {
             throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
@@ -204,7 +204,7 @@
      * @throws IllegalStateException if the package is not currently installed
      */
     void uninstallPackage(String packageName, int userId) {
-        final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+        final FakeDeviceState.Package pkg = mState.select(packageName, userId);
         if (pkg == null) {
             throw new IllegalStateException("package " + packageName+ " not installed");
         }
@@ -217,7 +217,7 @@
     }
 
     /** Represents the state of packages installed on a fake device. */
-    static class DummyDeviceState {
+    static class FakeDeviceState {
         private ArrayMap<String, Package> mPackages = new ArrayMap<>();
 
         void add(PackageBuilder pkgBuilder, int userId) {
@@ -333,16 +333,16 @@
         }
     }
 
-    final class DummyPackageManagerHelper implements PackageManagerHelper {
-        private final DummyDeviceState mState;
+    final class FakePackageManagerHelper implements PackageManagerHelper {
+        private final FakeDeviceState mState;
 
-        private DummyPackageManagerHelper(DummyDeviceState state) {
+        private FakePackageManagerHelper(FakeDeviceState state) {
             mState = state;
         }
 
         @Override
         public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(packageName, userId);
             if (pkg == null) {
                 return null;
             }
@@ -353,15 +353,15 @@
             pi.packageName = pkg.packageName;
             pi.overlayTarget = pkg.targetPackageName;
             pi.targetOverlayableName = pkg.targetOverlayableName;
-            pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
+            pi.overlayCategory = "Fake-category-" + pkg.targetPackageName;
             return pi;
         }
 
         @Override
         public boolean signaturesMatching(@NonNull String packageName1,
                 @NonNull String packageName2, int userId) {
-            final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId);
-            final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId);
+            final FakeDeviceState.Package pkg1 = mState.select(packageName1, userId);
+            final FakeDeviceState.Package pkg2 = mState.select(packageName2, userId);
             return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate);
         }
 
@@ -382,7 +382,7 @@
         @Override
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @NonNull String targetOverlayableName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(packageName, userId);
             if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) {
                 return null;
             }
@@ -403,7 +403,7 @@
 
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(targetPackageName, userId);
             return pkg != null && pkg.overlayableNames.contains(targetPackageName);
         }
 
@@ -413,16 +413,16 @@
         }
     }
 
-    static class DummyIdmapDaemon extends IdmapDaemon {
-        private final DummyDeviceState mState;
+    static class FakeIdmapDaemon extends IdmapDaemon {
+        private final FakeDeviceState mState;
         private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
 
-        DummyIdmapDaemon(DummyDeviceState state) {
+        FakeIdmapDaemon(FakeDeviceState state) {
             this.mState = state;
         }
 
         private int getCrc(@NonNull final String path) {
-            final DummyDeviceState.Package pkg = mState.selectFromPath(path);
+            final FakeDeviceState.Package pkg = mState.selectFromPath(path);
             Assert.assertNotNull(pkg);
             return pkg.versionCode;
         }
@@ -486,7 +486,7 @@
         }
     }
 
-    static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
+    static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
         public int count;
 
         public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 146f60a..9ef7557 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -50,55 +50,55 @@
     private OverlayManagerSettings mSettings;
 
     private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
-            "com.dummy.overlay_a",
-            "com.dummy.target",
+            "com.test.overlay_a",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_a-1/base.apk",
+            "/data/app/com.test.overlay_a-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
-            "com.dummy.overlay_b",
-            "com.dummy.target",
+            "com.test.overlay_b",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_b-1/base.apk",
+            "/data/app/com.test.overlay_b-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
-            "com.dummy.overlay_c",
-            "com.dummy.target",
+            "com.test.overlay_c",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_c-1/base.apk",
+            "/data/app/com.test.overlay_c-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
-            "com.dummy.overlay_a",
-            "com.dummy.target",
+            "com.test.overlay_a",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_a-1/base.apk",
+            "/data/app/com.test.overlay_a-1/base.apk",
             STATE_DISABLED,
             1,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
-            "com.dummy.overlay_b",
-            "com.dummy.target",
+            "com.test.overlay_b",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_b-1/base.apk",
+            "/data/app/com.test.overlay_b-1/base.apk",
             STATE_DISABLED,
             1,
             0,
@@ -230,11 +230,11 @@
         assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
 
         OverlayInfo otherTarget = new OverlayInfo(
-                "com.dummy.overlay_other",
-                "com.dummy.some.other.target",
+                "com.test.overlay_other",
+                "com.test.some.other.target",
                 null,
                 "some-category",
-                "/data/app/com.dummy.overlay_other-1/base.apk",
+                "/data/app/com.test.overlay_other-1/base.apk",
                 STATE_DISABLED,
                 0,
                 0,
@@ -350,7 +350,7 @@
         ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
 
         mSettings.restore(is);
-        assertDoesNotContain(mSettings, "com.dummy.overlay", 0);
+        assertDoesNotContain(mSettings, "com.test.overlay", 0);
     }
 
     @Test
@@ -359,27 +359,27 @@
         final String xml =
                 "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
                 + "<overlays version='" + version + "'>\n"
-                + "<item packageName='com.dummy.overlay'\n"
+                + "<item packageName='com.test.overlay'\n"
                 + "      userId='1234'\n"
-                + "      targetPackageName='com.dummy.target'\n"
-                + "      baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n"
+                + "      targetPackageName='com.test.target'\n"
+                + "      baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
                 + "      state='" + STATE_DISABLED + "'\n"
                 + "      isEnabled='false'\n"
-                + "      category='dummy-category'\n"
+                + "      category='test-category'\n"
                 + "      isStatic='false'\n"
                 + "      priority='0' />\n"
                 + "</overlays>\n";
         ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
 
         mSettings.restore(is);
-        OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234);
+        OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
         assertNotNull(oi);
-        assertEquals("com.dummy.overlay", oi.packageName);
-        assertEquals("com.dummy.target", oi.targetPackageName);
-        assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath);
+        assertEquals("com.test.overlay", oi.packageName);
+        assertEquals("com.test.target", oi.targetPackageName);
+        assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
         assertEquals(1234, oi.userId);
         assertEquals(STATE_DISABLED, oi.state);
-        assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234));
+        assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index dd85484..ad9692f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1523,7 +1523,8 @@
             // Return error to skip unnecessary operation.
             doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
                     any() /* window */,  any() /* attrs */,
-                    anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */,
+                    anyInt() /* viewVisibility */, anyInt() /* displayId */,
+                    any() /* requestedVisibility */, any() /* outFrame */,
                     any() /* outContentInsets */, any() /* outStableInsets */,
                     any() /* outDisplayCutout */, any() /* outInputChannel */,
                     any() /* outInsetsState */, any() /* outActiveControls */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 3349c6d..8292420 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -23,20 +23,24 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.EnterPipRequestedItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IDisplayWindowListener;
@@ -252,5 +256,36 @@
         assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
         assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
     }
+
+    @Test
+    public void testUpdateSleep() {
+        doCallRealMethod().when(mWm.mRoot).hasAwakeDisplay();
+        mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        topActivity.setState(Task.ActivityState.RESUMED, "test");
+
+        final Runnable assertTopNonSleeping = () -> {
+            assertFalse(mAtm.mInternal.isSleeping());
+            assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
+            assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+        };
+        assertTopNonSleeping.run();
+
+        // Sleep all displays.
+        mWm.mRoot.forAllDisplays(display -> doReturn(true).when(display).shouldSleep());
+        mAtm.updateSleepIfNeededLocked();
+
+        assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+        assertTrue(mAtm.mInternal.isSleeping());
+        assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
+                mAtm.mInternal.getTopProcessState());
+        assertNull(mAtm.mInternal.getTopApp());
+
+        // Wake all displays.
+        mWm.mRoot.forAllDisplays(display -> doReturn(false).when(display).shouldSleep());
+        mAtm.updateSleepIfNeededLocked();
+
+        assertTopNonSleeping.run();
+    }
 }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 2920c1d..5a14a24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -522,7 +522,9 @@
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
-        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.updateRequestedVisibility(requestedState);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -544,7 +546,9 @@
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
-        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.updateRequestedVisibility(requestedState);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         addWindow(mWindow);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index a55423a..21bdc9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -313,8 +313,8 @@
         // App requests to hide navigation bar.
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
-        mAppWindow.updateRequestedInsetsState(requestedState);
-        insetsPolicy.onInsetsModified(mAppWindow, requestedState);
+        mAppWindow.updateRequestedVisibility(requestedState);
+        insetsPolicy.onInsetsModified(mAppWindow);
         assertNotNull(displayPolicy.mInputConsumer);
 
         // App still requests to hide navigation bar, but without BEHAVIOR_SHOW_BARS_BY_TOUCH.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 8c02faf..d67120f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -38,7 +38,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -180,7 +179,7 @@
         final WindowState fullscreenApp = addWindow(TYPE_APPLICATION, "fullscreenApp");
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        fullscreenApp.updateRequestedInsetsState(requestedState);
+        fullscreenApp.updateRequestedVisibility(requestedState);
 
         // Add a non-fullscreen dialog window.
         final WindowState dialog = addWindow(TYPE_APPLICATION, "dialog");
@@ -215,7 +214,7 @@
         final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp");
         final InsetsState newRequestedState = new InsetsState();
         newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true);
-        newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState);
+        newFocusedFullscreenApp.updateRequestedVisibility(newRequestedState);
         // Make sure status bar is hidden by previous insets state.
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
 
@@ -232,20 +231,28 @@
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
-        addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
-                .getControllableInsetProvider().getSource().setVisible(false);
-        addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
-                .getControllableInsetProvider().getSource().setVisible(false);
+        final WindowState statusBar = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar");
+        statusBar.setHasSurface(true);
+        statusBar.getControllableInsetProvider().setServerVisible(true);
+        final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
+        navBar.setHasSurface(true);
+        navBar.getControllableInsetProvider().setServerVisible(true);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
 
-        doAnswer(invocation -> {
-            ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_STATUS_BAR, true);
-            ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_NAVIGATION_BAR, true);
-            return null;
-        }).when(policy).startAnimation(anyBoolean(), any(), any());
-
+        // Make both system bars invisible.
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
+        mAppWindow.updateRequestedVisibility(requestedState);
         policy.updateBarControlTarget(mAppWindow);
+        waitUntilWindowAnimatorIdle();
+        assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .getSource(ITYPE_STATUS_BAR).isVisible());
+        assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .getSource(ITYPE_NAVIGATION_BAR).isVisible());
+
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
         final InsetsSourceControl[] controls =
@@ -272,7 +279,7 @@
                 .getControllableInsetProvider().setServerVisible(true);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
@@ -301,7 +308,7 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
@@ -326,7 +333,8 @@
         assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible());
         assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
 
-        policy.onInsetsModified(mAppWindow, state);
+        mAppWindow.updateRequestedVisibility(state);
+        policy.onInsetsModified(mAppWindow);
         waitUntilWindowAnimatorIdle();
 
         controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -348,7 +356,7 @@
         final WindowState app2 = addWindow(TYPE_APPLICATION, "app");
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         final InsetsSourceControl[] controls =
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index a0fa936..9830631 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -205,7 +205,8 @@
         mProvider.updateControlForTarget(target, false /* force */);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR));
+        target.updateRequestedVisibility(state);
+        mProvider.updateClientVisibility(target);
         assertFalse(mSource.isVisible());
     }
 
@@ -217,7 +218,8 @@
         mProvider.setWindow(statusBar, null, null);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR));
+        target.updateRequestedVisibility(state);
+        mProvider.updateClientVisibility(target);
         assertTrue(mSource.isVisible());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index e2cd8a9..c14df67 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -189,8 +189,8 @@
         getController().onImeControlTargetChanged(mDisplayContent.mInputMethodInputTarget);
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_IME).setVisible(true);
-        mDisplayContent.mInputMethodInputTarget.updateRequestedInsetsState(requestedState);
-        getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget, requestedState);
+        mDisplayContent.mInputMethodInputTarget.updateRequestedVisibility(requestedState);
+        getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget);
 
         // Send our spy window (app) into the system so that we can detect the invocation.
         final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index c18043f..19bed48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -71,7 +71,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
-import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
@@ -426,10 +426,11 @@
                 .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
-        final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR);
-        source.setVisible(false);
+        final InsetsState state = new InsetsState();
+        state.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        app.updateRequestedVisibility(state);
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .onInsetsModified(app, source);
+                .updateClientVisibility(app);
         waitUntilHandlersIdle();
         assertFalse(statusBar.isVisible());
     }
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index a67273c..228f98e 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -477,6 +477,10 @@
     field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
   }
 
+  public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+  }
+
   public final class SmsCbCmasInfo implements android.os.Parcelable {
     ctor public SmsCbCmasInfo(int, int, int, int, int, int);
     method public int describeContents();
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 1376cdd..4a0f007 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -18,6 +18,7 @@
 
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
@@ -287,12 +288,12 @@
     }
 
     /**
-     * Copy constructors
+     * This constructor is used to create a copy of an existing SignalStrength object.
      *
      * @param s Source SignalStrength
-     *
      * @hide
      */
+    @SystemApi
     public SignalStrength(@NonNull SignalStrength s) {
         copyFrom(s);
     }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 579200e..41381c5 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -411,6 +411,25 @@
             };
 
     /**
+     * Convert handover failure mode to string.
+     *
+     * @param handoverFailureMode Handover failure mode
+     * @return Handover failure mode in string
+     *
+     * @hide
+     */
+    public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) {
+        switch (handoverFailureMode) {
+            case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown";
+            case HANDOVER_FAILURE_MODE_LEGACY: return "legacy";
+            case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one";
+            default: return Integer.toString(handoverFailureMode);
+        }
+    }
+
+    /**
      * Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new
      * instance.
      *
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8a05bdf..90a6de7 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.LongDef;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.Service;
@@ -30,12 +31,14 @@
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsServiceController;
 import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.SipTransportImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -105,18 +108,47 @@
 
     /**
      * This ImsService supports the capability to place emergency calls over MMTEL.
+     * <p>
+     * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
+     * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
+     * for this ImsService. If it is set, it will be removed during sanitization before the final
+     * capabilities bitfield is sent back to the framework.
      * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
      * adding other capabilities in a central location, so track this capability here as well.
      */
     public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
 
     /**
+     * This ImsService supports the capability to create SIP delegates for other IMS applications
+     * to use to proxy SIP messaging traffic through it.
+     * <p>
+     * In order for the framework to report SipDelegate creation as being available for this
+     * ImsService implementation, this ImsService must report this capability flag in
+     * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
+     * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
+     * {@link ImsFeature#FEATURE_RCS} features.
+     * @hide
+     */
+    public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
+
+    /**
+     * Used for internal correctness checks of capabilities set by the ImsService implementation and
+     * tracks the index of the largest defined flag in the capabilities long.
+     * @hide
+     */
+    public static final long CAPABILITY_MAX_INDEX =
+            Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION);
+
+    /**
      * @hide
      */
     @LongDef(flag = true,
             prefix = "CAPABILITY_",
             value = {
-                    CAPABILITY_EMERGENCY_OVER_MMTEL
+                    // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
+                    // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
+                    // not be set by users of ImsService.
+                    CAPABILITY_SIP_DELEGATE_CREATION
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ImsServiceCapability {}
@@ -127,6 +159,7 @@
      */
     private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
             put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+            put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
         }};
 
     /**
@@ -201,6 +234,17 @@
         }
 
         @Override
+        public long getImsServiceCapabilities() {
+            long caps = ImsService.this.getImsServiceCapabilities();
+            long sanitizedCaps = sanitizeCapabilities(caps);
+            if (caps != sanitizedCaps) {
+                Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+                        + Long.toHexString(caps ^ sanitizedCaps));
+            }
+            return sanitizedCaps;
+        }
+
+        @Override
         public void notifyImsServiceReadyForFeatureCreation() {
             ImsService.this.readyForFeatureCreation();
         }
@@ -218,6 +262,12 @@
         }
 
         @Override
+        public ISipTransport getSipTransport(int slotId) {
+            SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+            return s != null ? s.getBinder() : null;
+        }
+
+        @Override
         public void enableIms(int slotId) {
             ImsService.this.enableIms(slotId);
         }
@@ -371,6 +421,17 @@
     }
 
     /**
+     * The optional capabilities that this ImsService supports.
+     * <p>
+     * This should be a static configuration and should not change at runtime.
+     * @return The optional static capabilities of this ImsService implementation.
+     * @hide
+     */
+    public @ImsServiceCapability long getImsServiceCapabilities() {
+        return 0L;
+    }
+
+    /**
      * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
      * the ImsService has registered for with the framework, either in the manifest or via
      * {@link #querySupportedImsFeatures()}.
@@ -443,7 +504,37 @@
     }
 
     /**
-     * @return A string representation of the ImsService capabilties for logging.
+     * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
+     * <p>
+     * This is an optional interface used for devices that must support IMS single registration and
+     * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
+     * this method should return {@code null}. If this feature is supported, then this method must
+     * never be {@code null} and the optional ImsService capability flag
+     * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
+     * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
+     * supported for this ImsService.
+     * @param slotId The slot that is associated with the SipTransport implementation.
+     * @return the SipTransport implementation for the specified slot.
+     * @hide Keep this hidden until there is something to expose in SipTransport.
+     */
+    public @Nullable SipTransportImplBase getSipTransport(int slotId) {
+        return null;
+    }
+
+    private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
+        long filter = 0xFFFFFFFFFFFFFFFFL;
+        // pad the "allowed" set with zeros
+        filter <<= CAPABILITY_MAX_INDEX + 1;
+        // remove values above the allowed set.
+        caps &= ~filter;
+        // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
+        // internally.
+        caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
+        return caps;
+    }
+
+    /**
+     * @return A string representation of the ImsService capabilities for logging.
      * @hide
      */
     public static String getCapabilitiesString(@ImsServiceCapability long caps) {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c956cbc..c6966b3 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -21,6 +21,7 @@
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -34,6 +35,7 @@
     IImsMmTelFeature createMmTelFeature(int slotId);
     IImsRcsFeature createRcsFeature(int slotId);
     ImsFeatureConfiguration querySupportedImsFeatures();
+    long getImsServiceCapabilities();
     void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
@@ -41,6 +43,7 @@
     void removeImsFeature(int slotId, int featureType);
     IImsConfig getConfig(int slotId);
     IImsRegistration getRegistration(int slotId);
+    ISipTransport getSipTransport(int slotId);
     oneway void enableIms(int slotId);
     oneway void disableIms(int slotId);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
new file mode 100644
index 0000000..fe23343
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2020 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.telephony.ims.aidl;
+
+/**
+ * Interface for commands to the SIP Transport implementation.
+ * {@hide}
+ */
+interface ISipTransport {
+}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
new file mode 100644
index 0000000..17bd4b1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.aidl.ISipTransport;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
+ * IMS applications in order to support IMS single registration.
+ *
+ * @hide Until there is an implementation, keep this hidden
+ */
+public class SipTransportImplBase {
+
+    private final Executor mBinderExecutor;
+    private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
+
+    };
+
+    /**
+     * Create an implementation of SipTransportImplBase.
+     *
+     * @param executor The executor that remote calls from the framework should be called on.
+     */
+    public SipTransportImplBase(@NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+
+        mBinderExecutor = executor;
+    }
+
+    /**
+     * @return The IInterface used by the framework.
+     * @hide
+     */
+    public ISipTransport getBinder() {
+        return mSipTransportImpl;
+    }
+}
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
index b259679..80c1d43 100644
--- a/telephony/java/com/android/ims/ImsFeatureContainer.java
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -17,12 +17,14 @@
 package com.android.ims;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.feature.ImsFeature;
 
 import java.util.Objects;
@@ -49,6 +51,11 @@
     public final IImsRegistration imsRegistration;
 
     /**
+     * An optional interface containing the SIP transport implementation from the ImsService.
+     */
+    public final ISipTransport sipTransport;
+
+    /**
      * State of the feature that is being tracked.
      */
     private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
@@ -66,10 +73,11 @@
      * @param initialCaps The initial capabilities that the ImsService supports.
      */
     public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
-            @NonNull IImsRegistration iReg, long initialCaps) {
+            @NonNull IImsRegistration iReg, @Nullable ISipTransport transport, long initialCaps) {
         imsFeature = iFace;
         imsConfig = iConfig;
         imsRegistration = iReg;
+        sipTransport = transport;
         mCapabilities = initialCaps;
     }
 
@@ -80,6 +88,7 @@
         imsFeature = in.readStrongBinder();
         imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
         imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+        sipTransport = ISipTransport.Stub.asInterface(in.readStrongBinder());
         mState = in.readInt();
         mCapabilities = in.readLong();
     }
@@ -123,13 +132,15 @@
         return imsFeature.equals(that.imsFeature) &&
                 imsConfig.equals(that.imsConfig) &&
                 imsRegistration.equals(that.imsRegistration) &&
+                sipTransport.equals(that.sipTransport) &&
                 mState == that.getState() &&
                 mCapabilities == that.getCapabilities();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+        return Objects.hash(imsFeature, imsConfig, imsRegistration, sipTransport, mState,
+                mCapabilities);
     }
 
     @Override
@@ -138,6 +149,7 @@
                 "imsFeature=" + imsFeature +
                 ", imsConfig=" + imsConfig +
                 ", imsRegistration=" + imsRegistration +
+                ", sipTransport=" + sipTransport +
                 ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
                 ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
                 '}';
@@ -153,6 +165,7 @@
         dest.writeStrongBinder(imsFeature);
         dest.writeStrongInterface(imsConfig);
         dest.writeStrongInterface(imsRegistration);
+        dest.writeStrongInterface(sipTransport);
         dest.writeInt(mState);
         dest.writeLong(mCapabilities);
     }
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index bf7a6ff..725bfa9 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -280,7 +280,7 @@
         after.removeAll(before);
         // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
         assertThat(after).hasSize(1);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -336,31 +336,37 @@
         String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
-        pushString(TEST_STRING_1, oldFilePath1);
-        pushString(TEST_STRING_2, oldFilePath2);
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4;
-        pushString(TEST_STRING_3, newFilePath3);
-        pushString(TEST_STRING_4, newFilePath4);
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc/apexrollback");
@@ -368,7 +374,7 @@
         after.removeAll(before);
         // There should be only one /data/misc/apexrollback/<rollbackId> created during test
         assertThat(after).hasSize(1);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -384,32 +390,38 @@
                 APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        pushString(TEST_STRING_1, oldFilePath1);
-        pushString(TEST_STRING_2, oldFilePath2);
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
-        pushString(TEST_STRING_3, newFilePath3);
-        pushString(TEST_STRING_4, newFilePath4);
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback");
@@ -417,7 +429,7 @@
         after.removeAll(before);
         // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test
         assertThat(after).hasSize(1);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -432,31 +444,37 @@
         String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        pushString(TEST_STRING_1, oldFilePath1);
-        pushString(TEST_STRING_2, oldFilePath2);
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
-        pushString(TEST_STRING_3, newFilePath3);
-        pushString(TEST_STRING_4, newFilePath4);
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
@@ -464,7 +482,7 @@
         after.removeAll(before);
         // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
         assertThat(after).hasSize(1);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -478,30 +496,36 @@
         // Push files to apk data directory
         String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        pushString(TEST_STRING_1, oldFilePath1);
-        pushString(TEST_STRING_2, oldFilePath2);
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install version 2 of TESTAPP_A with rollback enabled
         runPhase("testRollbackApkDataDirectories_Phase2");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3;
         String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4;
-        pushString(TEST_STRING_3, newFilePath3);
-        pushString(TEST_STRING_4, newFilePath4);
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APK
         runPhase("testRollbackApkDataDirectories_Phase3");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
     }
 
     @Test
@@ -513,8 +537,10 @@
         String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        pushString(TEST_STRING_1, oldFilePath1);
-        pushString(TEST_STRING_2, oldFilePath2);
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
@@ -527,9 +553,11 @@
         assertThat(after).hasSize(1);
         // Expire all rollbacks and check CE snapshot directories are deleted
         runPhase("testCleanUp");
-        for (String dir : after) {
-            assertNull(getDevice().getFileEntry(dir));
-        }
+        runAsRoot(() -> {
+            for (String dir : after) {
+                assertNull(getDevice().getFileEntry(dir));
+            }
+        });
     }
 
     private void pushTestApex() throws Exception {
@@ -563,26 +591,34 @@
     }
 
     private List<String> getSnapshotDirectories(String baseDir) throws Exception {
-        IFileEntry f = getDevice().getFileEntry(baseDir);
-        if (f == null) {
-            Log.d(TAG, "baseDir doesn't exist: " + baseDir);
-            return Collections.EMPTY_LIST;
+        try {
+            getDevice().enableAdbRoot();
+            IFileEntry f = getDevice().getFileEntry(baseDir);
+            if (f == null) {
+                Log.d(TAG, "baseDir doesn't exist: " + baseDir);
+                return Collections.EMPTY_LIST;
+            }
+            List<String> list = f.getChildren(false)
+                    .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
+                    .map(entry -> entry.getFullPath())
+                    .collect(Collectors.toList());
+            Log.d(TAG, "getSnapshotDirectories=" + list);
+            return list;
+        } finally {
+            getDevice().disableAdbRoot();
         }
-        List<String> list = f.getChildren(false)
-                .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
-                .map(entry -> entry.getFullPath())
-                .collect(Collectors.toList());
-        Log.d(TAG, "getSnapshotDirectories=" + list);
-        return list;
     }
 
-    private void assertDirectoryIsEmpty(String path) {
+    private void assertDirectoryIsEmpty(String path) throws Exception {
         try {
+            getDevice().enableAdbRoot();
             IFileEntry file = getDevice().getFileEntry(path);
             assertTrue("Not a directory: " + path, file.isDirectory());
             assertTrue("Directory not empty: " + path, file.getChildren(false).isEmpty());
         } catch (DeviceNotAvailableException e) {
             fail("Can't access directory: " + path);
+        } finally {
+            getDevice().disableAdbRoot();
         }
     }
 
@@ -621,10 +657,15 @@
         }
     }
 
-    private void pushString(String contents, String deviceFilePath) throws Exception {
+    @FunctionalInterface
+    private interface ExceptionalRunnable {
+        void run() throws Exception;
+    }
+
+    private void runAsRoot(ExceptionalRunnable runnable) throws Exception {
         try {
             getDevice().enableAdbRoot();
-            assertThat(getDevice().pushString(contents, deviceFilePath)).isTrue();
+            runnable.run();
         } finally {
             getDevice().disableAdbRoot();
         }
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 46d680f..373aac6 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -25,6 +25,7 @@
         "junit",
         "mockito-target-minus-junit4",
         "net-tests-utils",
+        "net-utils-framework-common",
         "platform-test-annotations",
     ],
     libs: [
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 3c3076f..f52ab5b 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -32,7 +32,6 @@
 import static org.junit.Assert.fail;
 
 import android.net.LinkProperties.ProvisioningChange;
-import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.system.OsConstants;
 import android.util.ArraySet;
@@ -41,6 +40,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -447,23 +447,21 @@
         assertEquals(3, lp.getRoutes().size());
         assertAllRoutesHaveInterface("wlan0", lp);
 
-        // Check comparisons work.
+        // Check routes are updated correctly when calling setInterfaceName.
         LinkProperties lp2 = new LinkProperties(lp);
         assertAllRoutesHaveInterface("wlan0", lp2);
-        // LinkProperties#compareAllRoutes exists both in R and before R, but the return type
-        // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q.
-        if (isAtLeastR()) {
-            assertEquals(0, lp.compareAllRoutes(lp2).added.size());
-            assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
-        }
+        final CompareResult<RouteInfo> cr1 =
+                new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes());
+        assertEquals(0, cr1.added.size());
+        assertEquals(0, cr1.removed.size());
 
         lp2.setInterfaceName("p2p0");
         assertAllRoutesHaveInterface("p2p0", lp2);
         assertAllRoutesNotHaveInterface("wlan0", lp2);
-        if (isAtLeastR()) {
-            assertEquals(3, lp.compareAllRoutes(lp2).added.size());
-            assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
-        }
+        final CompareResult<RouteInfo> cr2 =
+                new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes());
+        assertEquals(3, cr2.added.size());
+        assertEquals(3, cr2.removed.size());
 
         // Remove route with incorrect interface, no route removed.
         lp.removeRoute(new RouteInfo(prefix2, null, null));
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 8e9d08c..2e1c29a 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -16,34 +16,50 @@
 
 package android.net;
 
+import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
+import android.content.res.Resources;
+import android.os.Build;
 import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.CollectionUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Random;
+import java.util.Set;
 
 /** Unit tests for {@link IpSecAlgorithm}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class IpSecAlgorithmTest {
-
     private static final byte[] KEY_MATERIAL;
 
+    private final Resources mMockResources = mock(Resources.class);
+
     static {
         KEY_MATERIAL = new byte[128];
         new Random().nextBytes(KEY_MATERIAL);
     };
 
+    private static byte[] generateKey(int keyLenInBits) {
+        return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8);
+    }
+
     @Test
     public void testNoTruncLen() throws Exception {
         Entry<String, Integer>[] authAndAeadList =
@@ -53,7 +69,7 @@
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
-                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224),
                 };
 
         // Expect auth and aead algorithms to throw errors if trunclen is omitted.
@@ -70,6 +86,52 @@
         new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
+    private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)
+            throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen);
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen));
+            fail("Expected exception on unprovided auth trunclen");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen);
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1);
+            fail("Invalid truncation length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen));
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8));
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    @Test
+    public void testValidationForAlgosAddedInS() throws Exception {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.R) {
+            return;
+        }
+
+        for (int len : new int[] {160, 224, 288}) {
+            checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
+        }
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
+    }
+
     @Test
     public void testTruncLenValidation() throws Exception {
         for (int truncLen : new int[] {256, 512}) {
@@ -127,4 +189,37 @@
         assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
         p.recycle();
     }
+
+    private static Set<String> getMandatoryAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    private static Set<String> getOptionalAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    @Test
+    public void testGetSupportedAlgorithms() throws Exception {
+        assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos()));
+        assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll(
+                IpSecAlgorithm.getSupportedAlgorithms()));
+    }
+
+    @Test
+    public void testLoadAlgos() throws Exception {
+        final Set<String> optionalAlgoSet = getOptionalAlgos();
+        final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
+
+        doReturn(optionalAlgos).when(mMockResources)
+                .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms);
+
+        final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
+        final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
+
+        assertEquals(expectedAlgos, enabledAlgos);
+    }
 }
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index f706118..d6e8922 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -584,7 +584,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
-    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -664,6 +664,8 @@
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7c2556d..fd4e1dd 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1626,6 +1626,14 @@
         private boolean mHasEverConnected;
 
         /**
+         * Boolean indicating if captive portal has never been detected on this network.
+         *
+         * This should be true by default, for newly created WifiConfigurations until a captive
+         * portal is detected.
+         */
+        private boolean mHasNeverDetectedCaptivePortal = true;
+
+        /**
          * set whether this network is visible in latest Qualified Network Selection
          * @param seen value set to candidate
          * @hide
@@ -1714,6 +1722,19 @@
             return mHasEverConnected;
         }
 
+        /**
+         * Set whether a captive portal has never been detected on this network.
+         * @hide
+         */
+        public void setHasNeverDetectedCaptivePortal(boolean value) {
+            mHasNeverDetectedCaptivePortal = value;
+        }
+
+        /** @hide */
+        public boolean hasNeverDetectedCaptivePortal() {
+            return mHasNeverDetectedCaptivePortal;
+        }
+
         /** @hide */
         public NetworkSelectionStatus() {
             // previously stored configs will not have this parameter, so we default to false.
@@ -1989,6 +2010,7 @@
             setCandidateScore(source.getCandidateScore());
             setConnectChoice(source.getConnectChoice());
             setHasEverConnected(source.hasEverConnected());
+            setHasNeverDetectedCaptivePortal(source.hasNeverDetectedCaptivePortal());
         }
 
         /** @hide */
@@ -2008,6 +2030,7 @@
                 dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
             }
             dest.writeInt(hasEverConnected() ? 1 : 0);
+            dest.writeInt(hasNeverDetectedCaptivePortal() ? 1 : 0);
         }
 
         /** @hide */
@@ -2026,6 +2049,7 @@
                 setConnectChoice(null);
             }
             setHasEverConnected(in.readInt() != 0);
+            setHasNeverDetectedCaptivePortal(in.readInt() != 0);
         }
     }
 
@@ -2287,6 +2311,8 @@
         }
         sbuf.append(" hasEverConnected: ")
                 .append(mNetworkSelectionStatus.hasEverConnected()).append("\n");
+        sbuf.append(" hasNeverDetectedCaptivePortal: ")
+                .append(mNetworkSelectionStatus.hasNeverDetectedCaptivePortal()).append("\n");
 
         if (this.numAssociation > 0) {
             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index e3800ad..da8e17e 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -191,14 +191,18 @@
     }
 
     /**
-     * Called when the discovered peer is no longer visible. All further operations on this
-     * discovery session will fail. If the peer is visible again,
+     * Called when the discovered service is not available. All further operations on this
+     * discovery session will fail. If the service is available again,
      * {@link #onServiceDiscovered(PeerHandle, byte[], List)} or
      * {@link #onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} will be called.
      *
      * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param reason Discovered service lost reason code. One of
+     *               {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE},
+     *               {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN
      */
-    public void onServiceLost(@NonNull PeerHandle peerHandle) {
+    public void onServiceLost(@NonNull PeerHandle peerHandle,
+            @WifiAwareManager.DiscoveryLostReasonCode int reason) {
         /* empty */
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index d6e46fd..03f5f40 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -151,6 +151,27 @@
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
 
+    /** @hide */
+    @IntDef({
+            WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN,
+            WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DiscoveryLostReasonCode {
+    }
+
+    /**
+     * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+     * indicating that the service was lost for unknown reason.
+     */
+    public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0;
+
+    /**
+     * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+     * indicating that the service advertised by the peer is no longer visible. This may be because
+     * the peer is out of range or because the peer stopped advertising this service.
+     */
+    public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1;
+
     private final Context mContext;
     private final IWifiAwareManager mService;
 
@@ -695,7 +716,8 @@
                             break;
                         case CALLBACK_MATCH_EXPIRED:
                             mOriginalCallback
-                                    .onServiceLost(new PeerHandle(msg.arg1));
+                                    .onServiceLost(new PeerHandle(msg.arg1),
+                                            WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE);
                     }
                 }
             };
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 5fe0cb4..d163fb0 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.aware;
 
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE;
 import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
 
 import static org.hamcrest.core.IsEqual.equalTo;
@@ -372,7 +373,8 @@
         // (5) discovery session is no longer visible
         sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
         mMockLooper.dispatchAll();
-        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+                eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
         assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
 
         // (6) terminate
@@ -520,7 +522,8 @@
         // (5) discovery session is no longer visible
         sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
         mMockLooper.dispatchAll();
-        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+                eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
         assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
 
         // (6) terminate