Merge "Refactor command line IME tracing"
diff --git a/Android.bp b/Android.bp
index 9b8e018..8164d6a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,9 +22,9 @@
     ],
     errorprone: {
         javacflags: [
-            "-Xep:AndroidFrameworkBinderIdentity:ERROR",
+            // "-Xep:AndroidFrameworkBinderIdentity:ERROR",
             "-Xep:AndroidFrameworkCompatChange:ERROR",
-            "-Xep:AndroidFrameworkUid:ERROR",
+            // "-Xep:AndroidFrameworkUid:ERROR",
             // NOTE: only enable to generate local patchfiles
             // "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster",
             // "-XepPatchLocation:/tmp/refaster/",
@@ -44,6 +44,7 @@
             "-Xep:AndroidFrameworkEfficientCollections:OFF",
             "-Xep:AndroidFrameworkEfficientParcelable:OFF",
             "-Xep:AndroidFrameworkEfficientStrings:OFF",
+            "-Xep:AndroidFrameworkEfficientXml:OFF",
         ],
     },
 }
@@ -527,6 +528,7 @@
         "android.hardware.vibrator-V1.3-java",
         "android.system.keystore2-java",
         "android.system.suspend.control.internal-java",
+        "cameraprotosnano",
         "devicepolicyprotosnano",
 
         "com.android.sysprop.apex",
@@ -626,6 +628,7 @@
         "av-types-aidl-java",
         "mediatranscoding_aidl_interface-java",
         "soundtrigger_middleware-aidl-java",
+        "modules-utils-os",
     ],
 }
 
@@ -1267,7 +1270,6 @@
 filegroup {
     name: "framework-telephony-common-shared-srcs",
     srcs: [
-        "core/java/android/os/BasicShellCommandHandler.java",
         "core/java/android/os/RegistrantList.java",
         "core/java/android/os/Registrant.java",
         "core/java/android/util/IndentingPrintWriter.java",
@@ -1350,7 +1352,6 @@
     name: "framework-wifi-service-shared-srcs",
     srcs: [
         "core/java/android/net/InterfaceConfiguration.java",
-        "core/java/android/os/BasicShellCommandHandler.java",
         "core/java/android/util/BackupUtils.java",
         "core/java/android/util/Rational.java",
         "core/java/com/android/internal/util/FastXmlSerializer.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49a42d7..380839e 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -70,6 +70,7 @@
         "android.hardware.cas-V1.2-java",
         "android.hardware.health-V1.0-java-constants",
         "android.hardware.radio-V1.5-java",
+        "android.hardware.radio-V1.6-java",
         "android.hardware.thermal-V1.0-java-constants",
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
@@ -252,21 +253,7 @@
         "framework-wifi.stubs",
         "private-stub-annotations-jar",
     ],
-    defaults: [
-        "android_defaults_stubs_current",
-        "android_stubs_dists_default",
-    ],
-    dist: {
-        dir: "apistubs/android/system",
-    },
-    dists: [
-        {
-            // Legacy dist path
-            targets: ["sdk", "win_sdk"],
-            tag: ".jar",
-            dest: "android_system.jar",
-        },
-    ],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
@@ -285,7 +272,21 @@
         "framework-wifi.stubs.system",
         "private-stub-annotations-jar",
     ],
-    defaults: ["android_defaults_stubs_current"],
+    defaults: [
+        "android_defaults_stubs_current",
+        "android_stubs_dists_default",
+    ],
+    dist: {
+        dir: "apistubs/android/system",
+    },
+    dists: [
+        {
+            // Legacy dist path
+            targets: ["sdk", "win_sdk"],
+            tag: ".jar",
+            dest: "android_system.jar",
+        },
+    ],
 }
 
 java_library_static {
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
new file mode 100644
index 0000000..5473690
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.graphics.perftests;
+
+import android.graphics.Typeface;
+import android.os.SharedMemory;
+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.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceSerializationPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testSerializeFontMap() throws Exception {
+        Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            Typeface.serializeFontMap(systemFontMap);
+        }
+    }
+
+    @Test
+    public void testDeserializeFontMap() throws Exception {
+        SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+        ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            buffer.position(0);
+            Typeface.deserializeFontMap(buffer);
+        }
+    }
+
+    @Test
+    public void testSetSystemFontMap() throws Exception {
+        SharedMemory memory = null;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
+            // (max_map_count).
+            Typeface.destroySystemFontMap();
+            Typeface.loadPreinstalledSystemFontMap();
+            if (memory != null) {
+                memory.close();
+            }
+            memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+            state.resumeTiming();
+            Typeface.setSystemFontMap(memory);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index d802177..2be873c 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -1 +1,143 @@
 // Signature format: 2.0
+package android.app.appsearch {
+
+  public final class AppSearchSchema {
+    method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
+    method @NonNull public String getSchemaType();
+  }
+
+  public static final class AppSearchSchema.Builder {
+    ctor public AppSearchSchema.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
+    method @NonNull public android.app.appsearch.AppSearchSchema build();
+  }
+
+  public static final class AppSearchSchema.PropertyConfig {
+    method public int getCardinality();
+    method public int getDataType();
+    method public int getIndexingType();
+    method @NonNull public String getName();
+    method @Nullable public String getSchemaType();
+    method public int getTokenizerType();
+    field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
+    field public static final int CARDINALITY_REPEATED = 1; // 0x1
+    field public static final int CARDINALITY_REQUIRED = 3; // 0x3
+    field public static final int DATA_TYPE_BOOLEAN = 4; // 0x4
+    field public static final int DATA_TYPE_BYTES = 5; // 0x5
+    field public static final int DATA_TYPE_DOCUMENT = 6; // 0x6
+    field public static final int DATA_TYPE_DOUBLE = 3; // 0x3
+    field public static final int DATA_TYPE_INT64 = 2; // 0x2
+    field public static final int DATA_TYPE_STRING = 1; // 0x1
+    field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1
+    field public static final int INDEXING_TYPE_NONE = 0; // 0x0
+    field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2
+    field public static final int TOKENIZER_TYPE_NONE = 0; // 0x0
+    field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1
+  }
+
+  public static final class AppSearchSchema.PropertyConfig.Builder {
+    ctor public AppSearchSchema.PropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setCardinality(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setDataType(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setIndexingType(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setSchemaType(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setTokenizerType(int);
+  }
+
+  public class GenericDocument {
+    ctor protected GenericDocument(@NonNull android.app.appsearch.GenericDocument);
+    method public long getCreationTimestampMillis();
+    method public static int getMaxIndexedProperties();
+    method @NonNull public String getNamespace();
+    method public boolean getPropertyBoolean(@NonNull String);
+    method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String);
+    method @Nullable public byte[] getPropertyBytes(@NonNull String);
+    method @Nullable public byte[][] getPropertyBytesArray(@NonNull String);
+    method @Nullable public android.app.appsearch.GenericDocument getPropertyDocument(@NonNull String);
+    method @Nullable public android.app.appsearch.GenericDocument[] getPropertyDocumentArray(@NonNull String);
+    method public double getPropertyDouble(@NonNull String);
+    method @Nullable public double[] getPropertyDoubleArray(@NonNull String);
+    method public long getPropertyLong(@NonNull String);
+    method @Nullable public long[] getPropertyLongArray(@NonNull String);
+    method @NonNull public java.util.Set<java.lang.String> getPropertyNames();
+    method @Nullable public String getPropertyString(@NonNull String);
+    method @Nullable public String[] getPropertyStringArray(@NonNull String);
+    method @NonNull public String getSchemaType();
+    method public int getScore();
+    method public long getTtlMillis();
+    method @NonNull public String getUri();
+    field public static final String DEFAULT_NAMESPACE = "";
+  }
+
+  public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
+    ctor public GenericDocument.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.app.appsearch.GenericDocument build();
+    method @NonNull public BuilderType setCreationTimestampMillis(long);
+    method @NonNull public BuilderType setNamespace(@NonNull String);
+    method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
+    method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
+    method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
+    method @NonNull public BuilderType setPropertyDouble(@NonNull String, @NonNull double...);
+    method @NonNull public BuilderType setPropertyLong(@NonNull String, @NonNull long...);
+    method @NonNull public BuilderType setPropertyString(@NonNull String, @NonNull java.lang.String...);
+    method @NonNull public BuilderType setScore(@IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int);
+    method @NonNull public BuilderType setTtlMillis(long);
+  }
+
+  public final class SearchResult {
+    method @NonNull public android.app.appsearch.GenericDocument getDocument();
+    method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
+  }
+
+  public static final class SearchResult.MatchInfo {
+    method @NonNull public CharSequence getExactMatch();
+    method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+    method @NonNull public String getFullText();
+    method @NonNull public String getPropertyPath();
+    method @NonNull public CharSequence getSnippet();
+    method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+  }
+
+  public static final class SearchResult.MatchRange {
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public final class SearchSpec {
+    method public int getMaxSnippetSize();
+    method @NonNull public java.util.List<java.lang.String> getNamespaces();
+    method public int getOrder();
+    method public int getRankingStrategy();
+    method public int getResultCountPerPage();
+    method @NonNull public java.util.List<java.lang.String> getSchemaTypes();
+    method public int getSnippetCount();
+    method public int getSnippetCountPerProperty();
+    method public int getTermMatch();
+    field public static final int ORDER_ASCENDING = 1; // 0x1
+    field public static final int ORDER_DESCENDING = 0; // 0x0
+    field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
+    field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
+    field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
+    field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
+    field public static final int TERM_MATCH_PREFIX = 2; // 0x2
+  }
+
+  public static final class SearchSpec.Builder {
+    ctor public SearchSpec.Builder();
+    method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.app.appsearch.SearchSpec build();
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setRankingStrategy(int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_NUM_PER_PAGE) int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_COUNT) int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_PER_PROPERTY_COUNT) int);
+    method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int);
+  }
+
+}
+
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
index beb9ad3..9ca363e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
@@ -16,10 +16,8 @@
 
 package android.app.appsearch;
 
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-
 import android.app.appsearch.AppSearchSchema.PropertyConfig;
 
 /**
@@ -29,9 +27,8 @@
  *
  * @hide
  */
-
 public class AppSearchEmail extends GenericDocument {
-    /** The name of the schema type for {@link AppSearchEmail} documents.*/
+    /** 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";
@@ -41,54 +38,55 @@
     private static final String KEY_SUBJECT = "subject";
     private static final String KEY_BODY = "body";
 
-    public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .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 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 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 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 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 PropertyConfig.Builder(KEY_BODY)
-                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                    .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                    .build()
-
-            ).build();
+    public static final AppSearchSchema SCHEMA =
+            new AppSearchSchema.Builder(SCHEMA_TYPE)
+                    .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 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 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 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 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 PropertyConfig.Builder(KEY_BODY)
+                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                    .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .build())
+                    .build();
 
     /**
-     * Creates a new {@link AppSearchEmail} from the contents of an existing
-     * {@link GenericDocument}.
+     * Creates a new {@link AppSearchEmail} from the contents of an existing {@link
+     * GenericDocument}.
      *
      * @param document The {@link GenericDocument} containing the email content.
      */
@@ -109,8 +107,8 @@
     /**
      * Gets the destination addresses of {@link AppSearchEmail}.
      *
-     * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not
-     *         been set yet.
+     * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not been
+     *     set yet.
      */
     @Nullable
     public String[] getTo() {
@@ -157,9 +155,7 @@
         return getPropertyString(KEY_BODY);
     }
 
-    /**
-     * The builder class for {@link AppSearchEmail}.
-     */
+    /** The builder class for {@link AppSearchEmail}. */
     public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
 
         /**
@@ -171,54 +167,42 @@
             super(uri, SCHEMA_TYPE);
         }
 
-        /**
-         * Sets the from address of {@link AppSearchEmail}
-         */
+        /** Sets the from address of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setFrom(@NonNull String from) {
             setPropertyString(KEY_FROM, from);
             return this;
         }
 
-        /**
-         * Sets the destination address of {@link AppSearchEmail}
-         */
+        /** Sets the destination address of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setTo(@NonNull String... to) {
             setPropertyString(KEY_TO, to);
             return this;
         }
 
-        /**
-         * Sets the CC list of {@link AppSearchEmail}
-         */
+        /** Sets the CC list of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setCc(@NonNull String... cc) {
             setPropertyString(KEY_CC, cc);
             return this;
         }
 
-        /**
-         * Sets the BCC list of {@link AppSearchEmail}
-         */
+        /** Sets the BCC list of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setBcc(@NonNull String... bcc) {
             setPropertyString(KEY_BCC, bcc);
             return this;
         }
 
-        /**
-         * Sets the subject of {@link AppSearchEmail}
-         */
+        /** Sets the subject of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setSubject(@NonNull String subject) {
             setPropertyString(KEY_SUBJECT, subject);
             return this;
         }
 
-        /**
-         * Sets the body of {@link AppSearchEmail}
-         */
+        /** Sets the body of {@link AppSearchEmail} */
         @NonNull
         public AppSearchEmail.Builder setBody(@NonNull String body) {
             setPropertyString(KEY_BODY, body);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
index 3933726..2db74a8 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
@@ -16,15 +16,14 @@
 
 package android.app.appsearch;
 
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-
+import android.annotation.SuppressLint;
 import android.app.appsearch.exceptions.IllegalSchemaException;
+import android.os.Bundle;
 import android.util.ArraySet;
+
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -40,7 +39,8 @@
  * <p>For example, an e-mail message or a music recording could be a schema type.
  *
  * <p>The schema consists of type information, properties, and config (like tokenization type).
- * @hide
+ *
+ * @see AppSearchManager#setSchema
  */
 public final class AppSearchSchema {
     private static final String SCHEMA_TYPE_FIELD = "schemaType";
@@ -49,7 +49,6 @@
     private final Bundle mBundle;
 
     /** @hide */
-    
     public AppSearchSchema(@NonNull Bundle bundle) {
         Preconditions.checkNotNull(bundle);
         mBundle = bundle;
@@ -57,9 +56,9 @@
 
     /**
      * Returns the {@link Bundle} populated by this builder.
+     *
      * @hide
      */
-    
     @NonNull
     public Bundle getBundle() {
         return mBundle;
@@ -72,7 +71,7 @@
 
     /** Returns the name of this schema type, e.g. Email. */
     @NonNull
-    public String getSchemaTypeName() {
+    public String getSchemaType() {
         return mBundle.getString(SCHEMA_TYPE_FIELD, "");
     }
 
@@ -144,8 +143,8 @@
     /**
      * Configuration for a single property (field) of a document type.
      *
-     * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be
-     * a property.
+     * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be a
+     * property.
      */
     public static final class PropertyConfig {
         private static final String NAME_FIELD = "name";
@@ -157,18 +156,20 @@
 
         /**
          * Physical data-types of the contents of the property.
+         *
          * @hide
          */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
-        @IntDef(value = {
-                DATA_TYPE_STRING,
-                DATA_TYPE_INT64,
-                DATA_TYPE_DOUBLE,
-                DATA_TYPE_BOOLEAN,
-                DATA_TYPE_BYTES,
-                DATA_TYPE_DOCUMENT,
-        })
+        @IntDef(
+                value = {
+                    DATA_TYPE_STRING,
+                    DATA_TYPE_INT64,
+                    DATA_TYPE_DOUBLE,
+                    DATA_TYPE_BOOLEAN,
+                    DATA_TYPE_BYTES,
+                    DATA_TYPE_DOCUMENT,
+                })
         @Retention(RetentionPolicy.SOURCE)
         public @interface DataType {}
 
@@ -181,23 +182,25 @@
         public static final int DATA_TYPE_BYTES = 5;
 
         /**
-         * Indicates that the property itself is an Document, making it part a hierarchical
-         * Document schema. Any property using this DataType MUST have a valid
-         * {@code schemaType}.
+         * Indicates that the property is itself a {@link GenericDocument}, making it part of a
+         * hierarchical schema. Any property using this DataType MUST have a valid {@link
+         * PropertyConfig#getSchemaType}.
          */
         public static final int DATA_TYPE_DOCUMENT = 6;
 
         /**
          * The cardinality of the property (whether it is required, optional or repeated).
+         *
          * @hide
          */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
-        @IntDef(value = {
-                CARDINALITY_REPEATED,
-                CARDINALITY_OPTIONAL,
-                CARDINALITY_REQUIRED,
-        })
+        @IntDef(
+                value = {
+                    CARDINALITY_REPEATED,
+                    CARDINALITY_OPTIONAL,
+                    CARDINALITY_REQUIRED,
+                })
         @Retention(RetentionPolicy.SOURCE)
         public @interface Cardinality {}
 
@@ -212,23 +215,25 @@
 
         /**
          * Encapsulates the configurations on how AppSearch should query/index these terms.
+         *
          * @hide
          */
-        @IntDef(value = {
-                INDEXING_TYPE_NONE,
-                INDEXING_TYPE_EXACT_TERMS,
-                INDEXING_TYPE_PREFIXES,
-        })
+        @IntDef(
+                value = {
+                    INDEXING_TYPE_NONE,
+                    INDEXING_TYPE_EXACT_TERMS,
+                    INDEXING_TYPE_PREFIXES,
+                })
         @Retention(RetentionPolicy.SOURCE)
         public @interface IndexingType {}
 
         /**
          * Content in this property will not be tokenized or indexed.
          *
-         * <p>Useful if the data type is not made up of terms (e.g.
-         * {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES}
-         * type). All the properties inside the nested property won't be indexed regardless of the
-         * value of {@code indexingType} for the nested properties.
+         * <p>Useful if the data type is not made up of terms (e.g. {@link
+         * PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} type). None
+         * of the properties inside the nested property will be indexed regardless of the value of
+         * {@code indexingType} for the nested properties.
          */
         public static final int INDEXING_TYPE_NONE = 0;
 
@@ -250,20 +255,22 @@
 
         /**
          * Configures how tokens should be extracted from this property.
+         *
          * @hide
          */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
-        @IntDef(value = {
-                TOKENIZER_TYPE_NONE,
-                TOKENIZER_TYPE_PLAIN,
-        })
+        @IntDef(
+                value = {
+                    TOKENIZER_TYPE_NONE,
+                    TOKENIZER_TYPE_PLAIN,
+                })
         @Retention(RetentionPolicy.SOURCE)
         public @interface TokenizerType {}
 
         /**
-         * It is only valid for tokenizer_type to be 'NONE' if the data type is
-         * {@link PropertyConfig#DATA_TYPE_DOCUMENT}.
+         * It is only valid for tokenizer_type to be 'NONE' if the data type is {@link
+         * PropertyConfig#DATA_TYPE_DOCUMENT}.
          */
         public static final int TOKENIZER_TYPE_NONE = 0;
 
@@ -295,8 +302,8 @@
         /**
          * Returns the logical schema-type of the contents of this property.
          *
-         * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}.
-         * Otherwise, it is {@code null}.
+         * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. Otherwise,
+         * it is {@code null}.
          */
         @Nullable
         public String getSchemaType() {
@@ -325,9 +332,10 @@
          *
          * <p>The following properties must be set, or {@link PropertyConfig} construction will
          * fail:
+         *
          * <ul>
-         *     <li>dataType
-         *     <li>cardinality
+         *   <li>dataType
+         *   <li>cardinality
          * </ul>
          *
          * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
@@ -359,8 +367,8 @@
             /**
              * The logical schema-type of the contents of this property.
              *
-             * <p>Only required when {@link #setDataType} is set to
-             * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
+             * <p>Only required when {@link #setDataType} is set to {@link #DATA_TYPE_DOCUMENT}.
+             * Otherwise, it is ignored.
              */
             @NonNull
             public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
index 48d3ac0..0056377 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
@@ -16,15 +16,14 @@
 
 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.annotation.SuppressLint;
 import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.util.Log;
+
 import com.android.internal.util.Preconditions;
 
 import java.lang.reflect.Array;
@@ -37,17 +36,20 @@
  * Represents a document unit.
  *
  * <p>Documents are constructed via {@link GenericDocument.Builder}.
- * @hide
+ *
+ * @see AppSearchManager#putDocuments
+ * @see AppSearchManager#getByUri
+ * @see AppSearchManager#query
  */
 public class GenericDocument {
     private static final String TAG = "GenericDocument";
 
-    /** The default empty namespace.*/
+    /** 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.
+     * 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;
 
@@ -78,47 +80,43 @@
     /**
      * 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}.
+     * <p>Indexed properties are properties where the {@link
+     * AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than {@link
+     * 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 {@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;
+    /**
+     * 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;
+    @NonNull private final String mUri;
+    @NonNull private final String mSchemaType;
     private final long mCreationTimestampMillis;
-    @Nullable
-    private Integer mHashCode;
+    @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.
+     *
+     * @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());
+        mCreationTimestampMillis =
+                mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
     }
 
     /**
@@ -132,9 +130,9 @@
 
     /**
      * Returns the {@link Bundle} populated by this builder.
+     *
      * @hide
      */
-    
     @NonNull
     public Bundle getBundle() {
         return mBundle;
@@ -158,7 +156,11 @@
         return mSchemaType;
     }
 
-    /** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */
+    /**
+     * Returns the creation timestamp of the {@link GenericDocument}, in milliseconds.
+     *
+     * <p>The value is in the {@link System#currentTimeMillis} time base.
+     */
     public long getCreationTimestampMillis() {
         return mCreationTimestampMillis;
     }
@@ -166,8 +168,12 @@
     /**
      * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
      *
+     * <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of
+     * {@code creationTimestampMillis + ttlMillis}, measured in the {@link System#currentTimeMillis}
+     * time base, the document will be auto-deleted.
+     *
      * <p>The default value is 0, which means the document is permanent and won't be auto-deleted
-     *    until the app is uninstalled.
+     * until the app is uninstalled.
      */
     public long getTtlMillis() {
         return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
@@ -179,7 +185,10 @@
      * <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.
+     * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
+     * Documents with higher scores are considered better than documents with lower scores.
+     *
+     * <p>Any nonnegative integer can be used a score.
      */
     public int getScore() {
         return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE);
@@ -195,8 +204,8 @@
      * 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.
+     * @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) {
@@ -214,7 +223,7 @@
      *
      * @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.
+     *     there is no such key or the value is of a different type.
      */
     public long getPropertyLong(@NonNull String key) {
         Preconditions.checkNotNull(key);
@@ -231,7 +240,7 @@
      *
      * @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.
+     *     if there is no such key or the value is of a different type.
      */
     public double getPropertyDouble(@NonNull String key) {
         Preconditions.checkNotNull(key);
@@ -247,8 +256,8 @@
      * 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.
+     * @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);
@@ -264,8 +273,8 @@
      * 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.
+     * @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) {
@@ -283,7 +292,7 @@
      *
      * @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.
+     *     there is no such key or the value is of a different type.
      */
     @Nullable
     public GenericDocument getPropertyDocument(@NonNull String key) {
@@ -300,10 +309,18 @@
     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().");
+            Log.w(
+                    TAG,
+                    "The value for \""
+                            + key
+                            + "\" contains "
+                            + propertyLength
+                            + " elements. Only the first one will be returned from "
+                            + "getProperty"
+                            + propertyType
+                            + "(). Try getProperty"
+                            + propertyType
+                            + "Array().");
         }
     }
 
@@ -311,8 +328,8 @@
      * 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.
+     * @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) {
@@ -324,8 +341,8 @@
      * 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.
+     * @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) {
@@ -337,8 +354,8 @@
      * 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.
+     * @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) {
@@ -350,8 +367,8 @@
      * 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.
+     * @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) {
@@ -363,8 +380,8 @@
      * 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.
+     * @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
@@ -397,7 +414,7 @@
      *
      * @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.
+     *     value is set or the value is of a different type.
      */
     @SuppressLint("ArrayReturn")
     @Nullable
@@ -419,8 +436,8 @@
     }
 
     /**
-     * Gets a repeated property of the given key, and casts it to the given class type, which
-     * must be an array class type.
+     * 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) {
@@ -449,8 +466,9 @@
     }
 
     /**
-     * Deeply checks two bundles are equally or not.
-     * <p> Two bundles will be considered equally if they contain same content.
+     * Deeply checks whether two bundles are equal.
+     *
+     * <p>Two bundles will be considered equal if they contain the same content.
      */
     @SuppressWarnings("unchecked")
     private static boolean bundleEquals(Bundle one, Bundle two) {
@@ -537,8 +555,9 @@
 
     /**
      * Calculates the hash code for a bundle.
-     * <p> The hash code is only effected by the contents in the bundle. Bundles will get
-     * consistent hash code if they have same contents.
+     *
+     * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent
+     * hash code if they have same contents.
      */
     @SuppressWarnings("unchecked")
     private static int bundleHashCode(Bundle bundle) {
@@ -663,11 +682,11 @@
          * 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 {@link AppSearchSession#setSchema} prior
-         *        to inserting a document of this {@code schemaType} into the AppSearch index using
-         *        {@link AppSearchSession#putDocuments}. Otherwise, the document will be
-         *        rejected by {@link AppSearchSession#putDocuments}.
+         * @param schemaType The schema type of the {@link GenericDocument}. 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}. Otherwise, the document will be rejected by {@link
+         *     AppSearchManager#putDocuments}.
          */
         @SuppressWarnings("unchecked")
         public Builder(@NonNull String uri, @NonNull String schemaType) {
@@ -678,16 +697,16 @@
             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.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);
         }
 
         /**
-         * Sets the app-defined namespace this Document resides in. No special values are
-         * reserved or understood by the infrastructure.
+         * Sets the app-defined namespace this Document resides in. No special values are reserved
+         * or understood by the infrastructure.
          *
          * <p>URIs are unique within a namespace.
          *
@@ -702,8 +721,13 @@
         /**
          * 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.
+         * <p>The score is a query-independent measure of the document's quality, relative to other
+         * {@link GenericDocument}s of the same type.
+         *
+         * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
+         * Documents with higher scores are considered better than documents with lower scores.
+         *
+         * <p>Any nonnegative integer can be used a score.
          *
          * @throws IllegalArgumentException If the provided value is negative.
          */
@@ -718,22 +742,28 @@
         }
 
         /**
-         * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds. Should be
-         * set using a value obtained from the {@link System#currentTimeMillis()} time base.
+         * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds.
+         *
+         * <p>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);
+            mBundle.putLong(
+                    GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis);
             return mBuilderTypeInstance;
         }
 
         /**
          * Sets 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.
+         * <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of
+         * {@code creationTimestampMillis + ttlMillis}, measured in the {@link
+         * System#currentTimeMillis} time base, the document will be auto-deleted.
+         *
+         * <p>The default value is 0, which means the document is permanent and won't be
+         * auto-deleted until the app is uninstalled.
          *
          * @param ttlMillis A non-negative duration in milliseconds.
          * @throws IllegalArgumentException If the provided value is negative.
@@ -749,8 +779,7 @@
         }
 
         /**
-         * Sets one or multiple {@code String} values for a property, replacing its previous
-         * values.
+         * 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.
@@ -781,8 +810,7 @@
         }
 
         /**
-         * Sets one or multiple {@code long} values for a property, replacing its previous
-         * values.
+         * 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.
@@ -797,8 +825,7 @@
         }
 
         /**
-         * Sets one or multiple {@code double} values for a property, replacing its previous
-         * values.
+         * 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.
@@ -851,9 +878,14 @@
                 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 + ".");
+                    throw new IllegalArgumentException(
+                            "The String at "
+                                    + i
+                                    + " length is: "
+                                    + values[i].length()
+                                    + ", which exceeds length limit: "
+                                    + MAX_STRING_LENGTH
+                                    + ".");
                 }
             }
             mProperties.putStringArray(key, values);
@@ -911,7 +943,10 @@
                 throw new IllegalArgumentException("The input array is empty.");
             } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
                 throw new IllegalArgumentException(
-                        "Repeated property \"" + key + "\" has length " + length
+                        "Repeated property \""
+                                + key
+                                + "\" has length "
+                                + length
                                 + ", which exceeds the limit of "
                                 + MAX_REPEATED_PROPERTY_LENGTH);
             }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
index e1e0eda..053d401 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.util.ArraySet;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.Arrays;
@@ -28,7 +29,7 @@
 /**
  * Encapsulates a request to retrieve documents by namespace and URI.
  *
- * @see AppSearchManager#getByUri
+ * @see AppSearchSession#getByUri
  * @hide
  */
 public final class GetByUriRequest {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
index 1f90bc1..42f1ff2 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
@@ -16,10 +16,10 @@
 
 package android.app.appsearch;
 
-import android.annotation.SuppressLint;
-
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.app.appsearch.exceptions.AppSearchException;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -29,9 +29,9 @@
 import java.util.List;
 
 /**
- * Encapsulates a request to index a document into an {@link AppSearchManager} database.
+ * Encapsulates a request to index a document into an {@link AppSearchSession} database.
  *
- * @see AppSearchManager#putDocuments
+ * @see AppSearchSession#putDocuments
  * @hide
  */
 public final class PutDocumentsRequest {
@@ -53,7 +53,7 @@
         private boolean mBuilt = false;
 
         /** Adds one or more documents to the request. */
-        @SuppressLint("MissingGetterMatchingBuilder")  // Merged list available from getDocuments()
+        @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
         @NonNull
         public Builder addGenericDocument(@NonNull GenericDocument... documents) {
             Preconditions.checkNotNull(documents);
@@ -61,7 +61,7 @@
         }
 
         /** Adds one or more documents to the request. */
-        @SuppressLint("MissingGetterMatchingBuilder")  // Merged list available from getDocuments()
+        @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
         @NonNull
         public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
index 486857f..3d83c39 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.util.ArraySet;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.Arrays;
@@ -28,7 +29,7 @@
 /**
  * Encapsulates a request to remove documents by namespace and URI.
  *
- * @see AppSearchManager#removeByUri
+ * @see AppSearchSession#removeByUri
  * @hide
  */
 public final class RemoveByUriRequest {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
index 99cb2f1..5ffa7c9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
@@ -16,60 +16,60 @@
 
 package android.app.appsearch;
 
-import android.os.Bundle;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 
-import java.util.Objects;
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
- * This class represents one of the results obtained from the query.
+ * This class represents one of the results obtained from an AppSearch 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
+ * <p>This allows clients to obtain:
+ *
+ * <ul>
+ *   <li>The document which matched, using {@link #getDocument}
+ *   <li>Information about which properties in the document matched, and "snippet" information
+ *       containing textual summaries of the document's matches, using {@link #getMatches}
+ * </ul>
+ *
+ * <p>"Snippet" refers to a substring of text from the content of document that is returned as a
+ * part of search result.
+ *
+ * @see SearchResults
  */
 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 mBundle;
 
-    @NonNull
-    private final Bundle mDocumentBundle;
+    @NonNull private final Bundle mDocumentBundle;
 
     /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */
-    @Nullable
-    private GenericDocument mDocument;
+    @Nullable private GenericDocument mDocument;
 
     /**
      * Contains a list of MatchInfo bundles that matched the request.
      *
-     * Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and
+     * <p>Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and
      * {@link SearchSpec.Builder#setSnippetCountPerProperty}.
      *
      * @see #getMatches()
      */
-    @NonNull
-    private final List<Bundle> mMatchBundles;
+    @NonNull private final List<Bundle> mMatchBundles;
 
     /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */
-    @Nullable
-    private List<MatchInfo> mMatches;
+    @Nullable private List<MatchInfo> mMatches;
 
     /** @hide */
-    
     public SearchResult(@NonNull Bundle bundle) {
         mBundle = Preconditions.checkNotNull(bundle);
         mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD));
@@ -77,7 +77,6 @@
     }
 
     /** @hide */
-    
     @NonNull
     public Bundle getBundle() {
         return mBundle;
@@ -85,6 +84,7 @@
 
     /**
      * Contains the matching {@link GenericDocument}.
+     *
      * @return Document object which matched the query.
      */
     @NonNull
@@ -98,10 +98,10 @@
     /**
      * Contains a list of Snippets that matched the request.
      *
-     * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using
-     * {@link SearchSpec.Builder#setSnippetCount} or
-     * {@link SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that
-     * value, this method returns an empty list.
+     * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link
+     *     SearchSpec.Builder#setSnippetCount} or {@link
+     *     SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that value, this
+     *     method returns an empty list.
      */
     @NonNull
     public List<MatchInfo> getMatches() {
@@ -116,79 +116,94 @@
     }
 
     /**
-     * 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.
+     * 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>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>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>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>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;
@@ -201,16 +216,18 @@
             mBundle = Preconditions.checkNotNull(bundle);
             Preconditions.checkNotNull(document);
             mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
-            mFullText = getPropertyValues(
-                    document, mPropertyPath, mBundle.getInt(VALUES_INDEX_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"
+         *
+         * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
+         * example 1 this returns "subject"
          */
         @NonNull
         public String getPropertyPath() {
@@ -219,6 +236,7 @@
 
         /**
          * 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."
          */
@@ -229,20 +247,23 @@
 
         /**
          * 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));
+                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.
+         * Gets the {@link MatchRange} corresponding to the given entry.
+         *
          * <p>For class example 1 this returns "foo"
          */
         @NonNull
@@ -252,26 +273,31 @@
 
         /**
          * Gets the snippet {@link MatchRange} corresponding to the given entry.
-         * <p>Only populated when set maxSnippetSize > 0 in
-         * {@link SearchSpec.Builder#setMaxSnippetSize}.
+         *
+         * <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));
+                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.
+         * 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
@@ -302,11 +328,10 @@
     /**
      * 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>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."
-     *
+     * <p>Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
      */
     public static final class MatchRange {
         private final int mEnd;
@@ -314,18 +339,18 @@
 
         /**
          * Creates a new immutable range.
-         * <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
+         *
+         * <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");
+                throw new IllegalArgumentException(
+                        "Start point must be less than or equal to " + "end point");
             }
             mStart = start;
             mEnd = end;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java
index 756d1b5..dbd09d6 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java
@@ -16,10 +16,9 @@
 
 package android.app.appsearch;
 
-import android.os.Bundle;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 
 import com.android.internal.util.Preconditions;
 
@@ -29,19 +28,17 @@
 
 /**
  * This class represents a page of {@link SearchResult}s
+ *
  * @hide
  */
-
 public class SearchResultPage {
     public static final String RESULTS_FIELD = "results";
     public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken";
     private final long mNextPageToken;
 
-    @Nullable
-    private List<SearchResult> mResults;
+    @Nullable private List<SearchResult> mResults;
 
-    @NonNull
-    private final Bundle mBundle;
+    @NonNull private final Bundle mBundle;
 
     public SearchResultPage(@NonNull Bundle bundle) {
         mBundle = Preconditions.checkNotNull(bundle);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
index 15acf10..68e31f0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -16,14 +16,14 @@
 
 package android.app.appsearch;
 
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
-
+import android.annotation.SuppressLint;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.app.appsearch.exceptions.IllegalSearchSpecException;
+import android.os.Bundle;
+
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -37,7 +37,6 @@
 /**
  * This class represents the specification logic for AppSearch. It can be used to set the type of
  * search, like prefix or exact only or apply filters to search for a specific schema type only etc.
- * @hide
  */
 // TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
 public final class SearchSpec {
@@ -52,10 +51,10 @@
     static final String MAX_SNIPPET_FIELD = "maxSnippet";
 
     /** @hide */
-    
     public static final int DEFAULT_NUM_PER_PAGE = 10;
 
-    // TODO(b/170371356): In framework, we may want these limits might be flag controlled.
+    // TODO(b/170371356): In framework, we may want these limits to be flag controlled.
+    //  If that happens, the @IntRange() directives in this class may have to change.
     private static final int MAX_NUM_PER_PAGE = 10_000;
     private static final int MAX_SNIPPET_COUNT = 10_000;
     private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000;
@@ -63,43 +62,45 @@
 
     /**
      * Term Match Type for the query.
+     *
      * @hide
      */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
-    @IntDef(value = {
-            TERM_MATCH_EXACT_ONLY,
-            TERM_MATCH_PREFIX
-    })
+    @IntDef(value = {TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TermMatch {}
 
     /**
      * Query terms will only match exact tokens in the index.
+     *
      * <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football".
      */
     public static final int TERM_MATCH_EXACT_ONLY = 1;
     /**
      * Query terms will match indexed tokens when the query term is a prefix of the token.
+     *
      * <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football".
      */
     public static final int TERM_MATCH_PREFIX = 2;
 
     /**
      * Ranking Strategy for query result.
+     *
      * @hide
      */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link ScoringSpecProto.RankingStrategy.Code}
-    @IntDef(value = {
-            RANKING_STRATEGY_NONE,
-            RANKING_STRATEGY_DOCUMENT_SCORE,
-            RANKING_STRATEGY_CREATION_TIMESTAMP
-    })
+    @IntDef(
+            value = {
+                RANKING_STRATEGY_NONE,
+                RANKING_STRATEGY_DOCUMENT_SCORE,
+                RANKING_STRATEGY_CREATION_TIMESTAMP
+            })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RankingStrategy {}
 
-    /** No Ranking, results are returned in arbitrary order.*/
+    /** No Ranking, results are returned in arbitrary order. */
     public static final int RANKING_STRATEGY_NONE = 0;
     /** Ranked by app-provided document scores. */
     public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1;
@@ -108,14 +109,12 @@
 
     /**
      * Order for query result.
+     *
      * @hide
      */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link ScoringSpecProto.Order.Code}
-    @IntDef(value = {
-            ORDER_DESCENDING,
-            ORDER_ASCENDING
-    })
+    @IntDef(value = {ORDER_DESCENDING, ORDER_ASCENDING})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Order {}
 
@@ -127,7 +126,6 @@
     private final Bundle mBundle;
 
     /** @hide */
-    
     public SearchSpec(@NonNull Bundle bundle) {
         Preconditions.checkNotNull(bundle);
         mBundle = bundle;
@@ -135,9 +133,9 @@
 
     /**
      * Returns the {@link Bundle} populated by this builder.
+     *
      * @hide
      */
-    
     @NonNull
     public Bundle getBundle() {
         return mBundle;
@@ -154,12 +152,12 @@
      * <p>If empty, the query will search over all schema types.
      */
     @NonNull
-    public List<String> getSchemas() {
-        List<String> schemas = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
-        if (schemas == null) {
+    public List<String> getSchemaTypes() {
+        List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
+        if (schemaTypes == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(schemas);
+        return Collections.unmodifiableList(schemaTypes);
     }
 
     /**
@@ -176,8 +174,8 @@
         return Collections.unmodifiableList(namespaces);
     }
 
-    /** Returns the number of results per page in the returned object. */
-    public int getNumPerPage() {
+    /** Returns the number of results per page in the result set. */
+    public int getResultCountPerPage() {
         return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
     }
 
@@ -222,14 +220,12 @@
             mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
         }
 
-        /**
-         * Indicates how the query terms should match {@code TermMatchCode} in the index.
-         */
+        /** Indicates how the query terms should match {@code TermMatchCode} in the index. */
         @NonNull
         public Builder setTermMatch(@TermMatch int termMatchTypeCode) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY,
-                    TERM_MATCH_PREFIX, "Term match type");
+            Preconditions.checkArgumentInRange(
+                    termMatchTypeCode, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type");
             mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode);
             return this;
         }
@@ -241,10 +237,10 @@
          * <p>If unset, the query will search over all schema types.
          */
         @NonNull
-        public Builder addSchema(@NonNull String... schemaTypes) {
+        public Builder addSchemaType(@NonNull String... schemaTypes) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            return addSchema(Arrays.asList(schemaTypes));
+            return addSchemaType(Arrays.asList(schemaTypes));
         }
 
         /**
@@ -254,7 +250,7 @@
          * <p>If unset, the query will search over all schema types.
          */
         @NonNull
-        public Builder addSchema(@NonNull Collection<String> schemaTypes) {
+        public Builder addSchemaType(@NonNull Collection<String> schemaTypes) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mSchemaTypes.addAll(schemaTypes);
@@ -262,8 +258,9 @@
         }
 
         /**
-         * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that
-         * have the specified namespaces.
+         * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have
+         * the specified namespaces.
+         *
          * <p>If unset, the query will search over all namespaces.
          */
         @NonNull
@@ -274,8 +271,9 @@
         }
 
         /**
-         * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that
-         * have the specified namespaces.
+         * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have
+         * the specified namespaces.
+         *
          * <p>If unset, the query will search over all namespaces.
          */
         @NonNull
@@ -288,51 +286,56 @@
 
         /**
          * Sets the number of results per page in the returned object.
-         * <p> The default number of results per page is 10. And should be set in range [0, 10k].
+         *
+         * <p>The default number of results per page is 10.
          */
         @NonNull
-        public SearchSpec.Builder setNumPerPage(int numPerPage) {
+        public SearchSpec.Builder setResultCountPerPage(
+                @IntRange(from = 0, to = MAX_NUM_PER_PAGE) int numPerPage) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkArgumentInRange(numPerPage, 0, MAX_NUM_PER_PAGE, "NumPerPage");
             mBundle.putInt(NUM_PER_PAGE_FIELD, numPerPage);
             return this;
         }
 
-        /** Sets ranking strategy for AppSearch results.*/
+        /** Sets ranking strategy for AppSearch results. */
         @NonNull
         public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
-                    RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy");
+            Preconditions.checkArgumentInRange(
+                    rankingStrategy,
+                    RANKING_STRATEGY_NONE,
+                    RANKING_STRATEGY_CREATION_TIMESTAMP,
+                    "Result ranking strategy");
             mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
             return this;
         }
 
         /**
-         * Indicates the order of returned search results, the default is DESC, meaning that results
-         * with higher scores come first.
+         * Indicates the order of returned search results, the default is {@link #ORDER_DESCENDING},
+         * meaning that results with higher scores come first.
+         *
          * <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}.
          */
         @NonNull
         public Builder setOrder(@Order int order) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
-                    "Result ranking order");
+            Preconditions.checkArgumentInRange(
+                    order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order");
             mBundle.putInt(ORDER_FIELD, order);
             return this;
         }
 
         /**
-         * Only the first {@code snippetCount} documents based on the ranking strategy
-         * will have snippet information provided.
+         * 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 SearchResult#getMatches} will
          * return {@code null} for that result.
-         *
-         * <p>The value should be set in range[0, 10k].
          */
         @NonNull
-        public SearchSpec.Builder setSnippetCount(int snippetCount) {
+        public SearchSpec.Builder setSnippetCount(
+                @IntRange(from = 0, to = MAX_SNIPPET_COUNT) int snippetCount) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount");
             mBundle.putInt(SNIPPET_COUNT_FIELD, snippetCount);
@@ -341,39 +344,40 @@
 
         /**
          * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty}
-         * snippets for a every property of {@link GenericDocument} will contain snippet
-         * information.
+         * snippets for each property of {@link GenericDocument} will contain snippet information.
          *
-         * <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].
+         * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return
+         * {@code null} for that result.
          */
         @NonNull
-        public SearchSpec.Builder setSnippetCountPerProperty(int snippetCountPerProperty) {
+        public SearchSpec.Builder setSnippetCountPerProperty(
+                @IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT)
+                        int snippetCountPerProperty) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkArgumentInRange(snippetCountPerProperty,
-                    0, MAX_SNIPPET_PER_PROPERTY_COUNT, "snippetCountPerProperty");
+            Preconditions.checkArgumentInRange(
+                    snippetCountPerProperty,
+                    0,
+                    MAX_SNIPPET_PER_PROPERTY_COUNT,
+                    "snippetCountPerProperty");
             mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, snippetCountPerProperty);
             return this;
         }
 
         /**
-         * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at
-         * {@code maxSnippetSize/2} bytes before the middle of the matching token and end at
-         * {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects
-         * token boundaries, therefore the returned window may be smaller than requested.
+         * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at {@code
+         * maxSnippetSize/2} bytes before the middle of the matching token and end at {@code
+         * maxSnippetSize/2} bytes after the middle of the matching token. It respects token
+         * boundaries, therefore the returned window may be smaller than requested.
          *
-         * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will
-         * be returned. If matches enabled is also set to false, then snippeting is disabled.
+         * <p>Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will be
+         * returned. If matches enabled is also set to false, then snippeting is disabled.
          *
          * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will
          * return a window of "bar baz bat" which is only 11 bytes long.
-         *
-         * <p>The value should be in range[0, 10k].
          */
         @NonNull
-        public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) {
+        public SearchSpec.Builder setMaxSnippetSize(
+                @IntRange(from = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkArgumentInRange(
                     maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize");
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
index f2c8156..3e472fd 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
@@ -16,11 +16,11 @@
 
 package android.app.appsearch;
 
-import android.annotation.SuppressLint;
-
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.util.ArraySet;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -30,9 +30,9 @@
 import java.util.Set;
 
 /**
- * Encapsulates a request to update the schema of an {@link AppSearchManager} database.
+ * Encapsulates a request to update the schema of an {@link AppSearchSession} database.
  *
- * @see AppSearchManager#setSchema
+ * @see AppSearchSession#setSchema
  * @hide
  */
 public final class SetSchemaRequest {
@@ -81,10 +81,10 @@
          * 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.
+         * <p>By default, this is {@code false} and schema incompatibility causes the {@link
+         * AppSearchSession#setSchema} call to fail.
          *
-         * @see AppSearchManager#setSchema
+         * @see AppSearchSession#setSchema
          */
         @NonNull
         public Builder setForceOverride(boolean forceOverride) {
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 15d0992..704f180 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
@@ -21,18 +21,19 @@
 import android.app.appsearch.AppSearchResult;
 
 /**
- * An exception thrown by {@code android.app.appsearch.AppSearchManager} or a subcomponent.
+ * An exception thrown by {@link android.app.appsearch.AppSearchSession} or a subcomponent.
  *
- * <p>These exceptions can be converted into a failed {@link AppSearchResult}
- * for propagating to the client.
+ * <p>These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the
+ * client.
+ *
  * @hide
  */
-//TODO(b/157082794): Linkify to AppSearchManager once that API is public
 public class AppSearchException extends Exception {
     private final @AppSearchResult.ResultCode int mResultCode;
 
     /**
      * Initializes an {@link AppSearchException} with no message.
+     *
      * @hide
      */
     public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
@@ -59,9 +60,7 @@
         return mResultCode;
     }
 
-    /**
-     * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
-     */
+    /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */
     @NonNull
     public <T> AppSearchResult<T> toAppSearchResult() {
         return AppSearchResult.newFailedResult(mResultCode, getMessage());
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
index 6dd86f5..5f8da7f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
@@ -18,14 +18,12 @@
 
 import android.annotation.NonNull;
 
-
 /**
  * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such
  * as unpopulated mandatory fields or illegal combinations of parameters.
  *
  * @hide
  */
-
 public class IllegalSchemaException extends IllegalArgumentException {
     /**
      * Constructs a new {@link IllegalSchemaException}.
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
index 3ef887f0..0b5dc2e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
@@ -18,14 +18,12 @@
 
 import android.annotation.NonNull;
 
-
 /**
- * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such
- * as unpopulated mandatory fields or illegal combinations of parameters.
+ * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such as
+ * unpopulated mandatory fields or illegal combinations of parameters.
  *
  * @hide
  */
-
 public class IllegalSearchSpecException extends IllegalArgumentException {
     /**
      * Constructs a new {@link IllegalSearchSpecException}.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index e0215449..247089b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -16,13 +16,7 @@
 
 package com.android.server.appsearch.external.localstorage;
 
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
 import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
 import android.annotation.WorkerThread;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
@@ -30,6 +24,12 @@
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
@@ -62,7 +62,6 @@
 import java.io.File;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -77,55 +76,65 @@
  *
  * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
  * are physically saved together in {@link IcingSearchEngine}, but logically isolated:
+ *
  * <ul>
- *      <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into
- *          SchemaTypes set in {@link #setSchema}.
- *      <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and
- *          save to namespaces set in {@link #putDocument}.
- *      <li>Remove database name prefix when retrieve documents in {@link #getDocument} and
- *          {@link #query}.
- *      <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of
- *          the queried database when user using empty filters in {@link #query}.
+ *   <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into SchemaTypes
+ *       set in {@link #setSchema}.
+ *   <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and save
+ *       to namespaces set in {@link #putDocument}.
+ *   <li>Remove database name prefix when retrieve documents in {@link #getDocument} and {@link
+ *       #query}.
+ *   <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of the
+ *       queried database when user using empty filters in {@link #query}.
  * </ul>
  *
  * <p>Methods in this class belong to two groups, the query group and the mutate group.
+ *
  * <ul>
- *     <li>All methods are going to modify global parameters and data in Icing are executed under
- *         WRITE lock to keep thread safety.
- *     <li>All methods are going to access global parameters or query data from Icing are executed
- *         under READ lock to improve query performance.
+ *   <li>All methods are going to modify global parameters and data in Icing are executed under
+ *       WRITE lock to keep thread safety.
+ *   <li>All methods are going to access global parameters or query data from Icing are executed
+ *       under READ lock to improve query performance.
  * </ul>
  *
  * <p>This class is thread safe.
  *
  * @hide
  */
-
 @WorkerThread
 public final class AppSearchImpl {
     private static final String TAG = "AppSearchImpl";
-    private static final char DATABASE_DELIMITER = '/';
 
-    @VisibleForTesting
-    static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
-    @VisibleForTesting
-    static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
-    @VisibleForTesting
-    static final int CHECK_OPTIMIZE_INTERVAL = 100;
+    @VisibleForTesting static final char DATABASE_DELIMITER = '/';
+
+    @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
+    @VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
+    @VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
 
     private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
-    private final IcingSearchEngine mIcingSearchEngine;
+
+    @GuardedBy("mReadWriteLock")
+    private final IcingSearchEngine mIcingSearchEngineLocked;
+
+    @GuardedBy("mReadWriteLock")
+    private final VisibilityStore mVisibilityStoreLocked;
 
     // The map contains schemaTypes and namespaces for all database. All values in the map have
-    // been already added database name prefix.
-    private final Map<String, Set<String>> mSchemaMap = new HashMap<>();
-    private final Map<String, Set<String>> mNamespaceMap = new HashMap<>();
+    // the database name prefix.
+    // TODO(b/172360376): Check if this can be replaced with an ArrayMap
+    @GuardedBy("mReadWriteLock")
+    private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>();
+
+    // TODO(b/172360376): Check if this can be replaced with an ArrayMap
+    @GuardedBy("mReadWriteLock")
+    private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
 
     /**
-     * The counter to check when to call {@link #checkForOptimize(boolean)}. The interval is
+     * The counter to check when to call {@link #checkForOptimizeLocked(boolean)}. The interval is
      * {@link #CHECK_OPTIMIZE_INTERVAL}.
      */
-    private int mOptimizeIntervalCount = 0;
+    @GuardedBy("mReadWriteLock")
+    private int mOptimizeIntervalCountLocked = 0;
 
     /**
      * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
@@ -134,44 +143,77 @@
     @NonNull
     public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
         Preconditions.checkNotNull(icingDir);
-        return new AppSearchImpl(icingDir);
+        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir);
+        appSearchImpl.initializeVisibilityStore();
+        return appSearchImpl;
     }
 
     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.
-            IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
-                    .setBaseDir(icingDir.getAbsolutePath()).build();
-            mIcingSearchEngine = new IcingSearchEngine(options);
+            IcingSearchEngineOptions options =
+                    IcingSearchEngineOptions.newBuilder()
+                            .setBaseDir(icingDir.getAbsolutePath())
+                            .build();
+            mIcingSearchEngineLocked = new IcingSearchEngine(options);
 
-            InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize();
+            InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
             SchemaProto schemaProto = null;
             GetAllNamespacesResultProto getAllNamespacesResultProto = null;
             try {
                 checkSuccess(initializeResultProto.getStatus());
-                schemaProto = getSchemaProto();
-                getAllNamespacesResultProto = mIcingSearchEngine.getAllNamespaces();
+                schemaProto = getSchemaProtoLocked();
+                getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces();
                 checkSuccess(getAllNamespacesResultProto.getStatus());
             } catch (AppSearchException e) {
+                Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
                 // Some error. Reset and see if it fixes it.
                 reset();
                 isReset = true;
             }
+
+            // Populate schema map
             for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
                 String qualifiedSchemaType = schema.getSchemaType();
-                addToMap(mSchemaMap, getDatabaseName(qualifiedSchemaType), qualifiedSchemaType);
+                addToMap(
+                        mSchemaMapLocked,
+                        getDatabaseName(qualifiedSchemaType),
+                        qualifiedSchemaType);
             }
+
+            // Populate namespace map
             for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
-                addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
+                addToMap(
+                        mNamespaceMapLocked,
+                        getDatabaseName(qualifiedNamespace),
+                        qualifiedNamespace);
             }
+
             // 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);
+                checkForOptimizeLocked(/* force= */ true);
             }
+
+            mVisibilityStoreLocked = new VisibilityStore(this);
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Initialize the visibility store in AppSearchImpl.
+     *
+     * @throws AppSearchException on IcingSearchEngine error.
+     */
+    void initializeVisibilityStore() throws AppSearchException {
+        mReadWriteLock.writeLock().lock();
+        try {
+            mVisibilityStoreLocked.initialize();
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -182,35 +224,36 @@
      *
      * <p>This method belongs to mutate group.
      *
-     * @param databaseName  The name of the database where this schema lives.
-     * @param schemas       Schemas to set for this app.
+     * @param databaseName The name of the database where this schema lives.
+     * @param schemas Schemas to set for this app.
      * @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.
+     *     which do not comply with the new schema will be deleted.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas,
-            boolean forceOverride) throws AppSearchException {
-        SchemaProto schemaProto = getSchemaProto();
-
-        SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
-
-        SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
-        for (AppSearchSchema schema : schemas) {
-            SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
-            newSchemaBuilder.addTypes(schemaTypeProto);
-        }
-
-        // Combine the existing schema (which may have types from other databases) with this
-        // database's new schema. Modifies the existingSchemaBuilder.
-        Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder,
-                newSchemaBuilder.build());
-
-        SetSchemaResultProto setSchemaResultProto;
+    public void setSchema(
+            @NonNull String databaseName,
+            @NonNull Set<AppSearchSchema> schemas,
+            boolean forceOverride)
+            throws AppSearchException {
         mReadWriteLock.writeLock().lock();
         try {
+            SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder();
+
+            SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
+            for (AppSearchSchema schema : schemas) {
+                SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
+                newSchemaBuilder.addTypes(schemaTypeProto);
+            }
+
+            // Combine the existing schema (which may have types from other databases) with this
+            // database's new schema. Modifies the existingSchemaBuilder.
+            RewrittenSchemaResults rewrittenSchemaResults =
+                    rewriteSchema(databaseName, existingSchemaBuilder, newSchemaBuilder.build());
+
             // Apply schema
-            setSchemaResultProto =
-                    mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
+            SetSchemaResultProto setSchemaResultProto =
+                    mIcingSearchEngineLocked.setSchema(
+                            existingSchemaBuilder.build(), forceOverride);
 
             // Determine whether it succeeded.
             try {
@@ -219,11 +262,12 @@
                 // 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();
+                    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;
@@ -231,16 +275,18 @@
             }
 
             // Update derived data structures.
-            mSchemaMap.put(databaseName, newTypeNames);
+            mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
+            mVisibilityStoreLocked.updateSchemas(
+                    databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes);
 
             // Determine whether to schedule an immediate optimize.
             if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
                     || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
-                    && forceOverride)) {
+                            && forceOverride)) {
                 // Any existing schemas which is not in 'schemas' will be deleted, and all
                 // documents of these types were also deleted. And so well if we force override
                 // incompatible schemas.
-                checkForOptimize(/* force= */true);
+                checkForOptimizeLocked(/* force= */ true);
             }
         } finally {
             mReadWriteLock.writeLock().unlock();
@@ -248,28 +294,63 @@
     }
 
     /**
+     * Update the visibility settings for this app.
+     *
+     * <p>This method belongs to the mutate group
+     *
+     * @param databaseName The name of the database where the visibility settings will apply.
+     * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces
+     * @throws AppSearchException on IcingSearchEngine error
+     */
+    public void setVisibility(
+            @NonNull String databaseName, @NonNull Set<String> schemasHiddenFromPlatformSurfaces)
+            throws AppSearchException {
+        mReadWriteLock.writeLock().lock();
+        try {
+            String databasePrefix = getDatabasePrefix(databaseName);
+            Set<String> qualifiedSchemasHiddenFromPlatformSurface =
+                    new ArraySet<>(schemasHiddenFromPlatformSurfaces.size());
+            for (String schema : schemasHiddenFromPlatformSurfaces) {
+                Set<String> existingSchemas = mSchemaMapLocked.get(databaseName);
+                if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) {
+                    throw new AppSearchException(
+                            AppSearchResult.RESULT_NOT_FOUND,
+                            "Unknown schema(s): "
+                                    + schemasHiddenFromPlatformSurfaces
+                                    + " provided during setVisibility.");
+                }
+                qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema);
+            }
+            mVisibilityStoreLocked.setVisibility(
+                    databaseName, qualifiedSchemasHiddenFromPlatformSurface);
+        } finally {
+            mReadWriteLock.writeLock().lock();
+        }
+    }
+
+    /**
      * Adds a document to the AppSearch index.
      *
      * <p>This method belongs to mutate group.
      *
      * @param databaseName The databaseName this document resides in.
-     * @param document     The document to index.
+     * @param document The document to index.
      * @throws AppSearchException on IcingSearchEngine error.
      */
     public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document)
             throws AppSearchException {
-        DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.convert(
-                document).toBuilder();
+        DocumentProto.Builder documentBuilder =
+                GenericDocumentToProtoConverter.convert(document).toBuilder();
         addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName));
 
         PutResultProto putResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            putResultProto = mIcingSearchEngine.put(documentBuilder.build());
-            addToMap(mNamespaceMap, databaseName, documentBuilder.getNamespace());
+            putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
+            addToMap(mNamespaceMapLocked, databaseName, documentBuilder.getNamespace());
             // The existing documents with same URI will be deleted, so there maybe some resources
             // could be released after optimize().
-            checkForOptimize(/* force= */false);
+            checkForOptimizeLocked(/* force= */ false);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -282,19 +363,20 @@
      * <p>This method belongs to query group.
      *
      * @param databaseName The databaseName this document resides in.
-     * @param namespace    The namespace this document resides in.
-     * @param uri          The URI of the document to get.
+     * @param namespace The namespace this document resides in.
+     * @param uri The URI of the document to get.
      * @return The Document contents
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
-    public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException {
+    public GenericDocument getDocument(
+            @NonNull String databaseName, @NonNull String namespace, @NonNull String uri)
+            throws AppSearchException {
         GetResultProto getResultProto;
         mReadWriteLock.readLock().lock();
         try {
-            getResultProto = mIcingSearchEngine.get(
-                    getDatabasePrefix(databaseName) + namespace, uri);
+            getResultProto =
+                    mIcingSearchEngineLocked.get(getDatabasePrefix(databaseName) + namespace, uri);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
@@ -310,19 +392,25 @@
      *
      * <p>This method belongs to query group.
      *
-     * @param databaseName    The databaseName this query for.
+     * @param databaseName The databaseName this query for.
      * @param queryExpression Query String to search.
-     * @param searchSpec      Spec for setting filters, raw query etc.
-     * @return The results of performing this search. It may contain an empty list of results if
-     * no documents matched the query.
+     * @param searchSpec Spec for setting filters, raw query etc.
+     * @return The results of performing this search. It may contain an empty list of results if no
+     *     documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
     public SearchResultPage query(
             @NonNull String databaseName,
             @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec) throws AppSearchException {
-        return doQuery(Collections.singleton(databaseName), queryExpression, searchSpec);
+            @NonNull SearchSpec searchSpec)
+            throws AppSearchException {
+        mReadWriteLock.readLock().lock();
+        try {
+            return doQueryLocked(Collections.singleton(databaseName), queryExpression, searchSpec);
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
     }
 
     /**
@@ -332,45 +420,54 @@
      * <p>This method belongs to query group.
      *
      * @param queryExpression Query String to search.
-     * @param searchSpec      Spec for setting filters, raw query etc.
-     * @return The results of performing this search. It may contain an empty list of results if
-     * no documents matched the query.
+     * @param searchSpec Spec for setting filters, raw query etc.
+     * @return The results of performing this search. It may contain an empty list of results if no
+     *     documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
     public SearchResultPage globalQuery(
-            @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec) throws AppSearchException {
-        return doQuery(mNamespaceMap.keySet(), queryExpression, searchSpec);
+            @NonNull String queryExpression, @NonNull SearchSpec searchSpec)
+            throws AppSearchException {
+        // TODO(b/169883602): Check if the platform is querying us at a higher level. At this
+        //  point, we should add all platform-surfaceable schemas assuming the querier has been
+        //  verified.
+        mReadWriteLock.readLock().lock();
+        try {
+            // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
+            // that could exist.
+            return doQueryLocked(mNamespaceMapLocked.keySet(), queryExpression, searchSpec);
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
     }
 
-    private SearchResultPage doQuery(
-            @NonNull Set<String> databases, @NonNull String queryExpression,
+    @GuardedBy("mReadWriteLock")
+    private SearchResultPage doQueryLocked(
+            @NonNull Set<String> databases,
+            @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec)
             throws AppSearchException {
-        SearchSpecProto searchSpecProto =
-                SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
-        SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder()
-                .setQuery(queryExpression);
+        SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
+        SearchSpecProto.Builder searchSpecBuilder =
+                searchSpecProto.toBuilder().setQuery(queryExpression);
 
         ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec);
         ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
         SearchResultProto searchResultProto;
-        mReadWriteLock.readLock().lock();
-        try {
-            // rewriteSearchSpecForDatabases will return false if none of the databases have
-            // documents, so we can return an empty SearchResult and skip sending request to Icing.
-            // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
-            // that could exist.
-            if (!rewriteSearchSpecForDatabases(searchSpecBuilder, databases)) {
-                return new SearchResultPage(Bundle.EMPTY);
-            }
-            searchResultProto = mIcingSearchEngine.search(
-                    searchSpecBuilder.build(), scoringSpec, resultSpec);
-        } finally {
-            mReadWriteLock.readLock().unlock();
+
+        // rewriteSearchSpecForDatabases will return false if none of the databases that the
+        // client is trying to search on exist, so we can return an empty SearchResult and skip
+        // sending request to Icing.
+        // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
+        // that could exist.
+        if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder, databases)) {
+            return new SearchResultPage(Bundle.EMPTY);
         }
+        searchResultProto =
+                mIcingSearchEngineLocked.search(searchSpecBuilder.build(), scoringSpec, resultSpec);
         checkSuccess(searchResultProto.getStatus());
+
         return rewriteSearchResultProto(searchResultProto);
     }
 
@@ -385,11 +482,16 @@
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
-    public SearchResultPage getNextPage(long nextPageToken)
-            throws AppSearchException {
-        SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken);
-        checkSuccess(searchResultProto.getStatus());
-        return rewriteSearchResultProto(searchResultProto);
+    public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException {
+        mReadWriteLock.readLock().lock();
+        try {
+            SearchResultProto searchResultProto =
+                    mIcingSearchEngineLocked.getNextPage(nextPageToken);
+            checkSuccess(searchResultProto.getStatus());
+            return rewriteSearchResultProto(searchResultProto);
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
     }
 
     /**
@@ -398,10 +500,15 @@
      * <p>This method belongs to query group.
      *
      * @param nextPageToken The token of pre-loaded results of previously executed query to be
-     *                      Invalidated.
+     *     Invalidated.
      */
     public void invalidateNextPageToken(long nextPageToken) {
-        mIcingSearchEngine.invalidateNextPageToken(nextPageToken);
+        mReadWriteLock.readLock().lock();
+        try {
+            mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
     }
 
     /**
@@ -410,18 +517,18 @@
      * <p>This method belongs to mutate group.
      *
      * @param databaseName The databaseName the document is in.
-     * @param namespace    Namespace of the document to remove.
-     * @param uri          URI of the document to remove.
+     * @param namespace Namespace of the document to remove.
+     * @param uri URI of the document to remove.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void remove(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException {
+    public void remove(@NonNull String databaseName, @NonNull String namespace, @NonNull String uri)
+            throws AppSearchException {
         String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            deleteResultProto = mIcingSearchEngine.delete(qualifiedNamespace, uri);
-            checkForOptimize(/* force= */false);
+            deleteResultProto = mIcingSearchEngineLocked.delete(qualifiedNamespace, uri);
+            checkForOptimizeLocked(/* force= */ false);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -433,39 +540,38 @@
      *
      * <p>This method belongs to mutate group.
      *
-     * @param databaseName    The databaseName the document is in.
+     * @param databaseName The databaseName the document is in.
      * @param queryExpression Query String to search.
-     * @param searchSpec      Defines what and how to remove
+     * @param searchSpec Defines what and how to remove
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression,
+    public void removeByQuery(
+            @NonNull String databaseName,
+            @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec)
             throws AppSearchException {
-
-        SearchSpecProto searchSpecProto =
-                SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
-        SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder()
-                .setQuery(queryExpression);
+        SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
+        SearchSpecProto.Builder searchSpecBuilder =
+                searchSpecProto.toBuilder().setQuery(queryExpression);
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
         try {
             // Only rewrite SearchSpec for non empty database.
             // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we
             // should skip sending request to Icing and return in here.
-            if (!rewriteSearchSpecForDatabases(searchSpecBuilder,
-                    Collections.singleton(databaseName))) {
+            if (!rewriteSearchSpecForDatabasesLocked(
+                    searchSpecBuilder, Collections.singleton(databaseName))) {
                 return;
             }
-            deleteResultProto = mIcingSearchEngine.deleteByQuery(
-                    searchSpecBuilder.build());
-            checkForOptimize(/* force= */true);
+            deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
+            checkForOptimizeLocked(/* force= */ true);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
         // It seems that the caller wants to get success if the data matching the query is not in
         // the DB because it was not there or was successfully deleted.
-        checkCodeOneOf(deleteResultProto.getStatus(),
-                StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
+        checkCodeOneOf(
+                deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
     }
 
     /**
@@ -475,36 +581,53 @@
      *
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    @VisibleForTesting
-    public void reset() throws AppSearchException {
+    private void reset() throws AppSearchException {
         ResetResultProto resetResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            resetResultProto = mIcingSearchEngine.reset();
-            mOptimizeIntervalCount = 0;
-            mSchemaMap.clear();
-            mNamespaceMap.clear();
+            resetResultProto = mIcingSearchEngineLocked.reset();
+            mOptimizeIntervalCountLocked = 0;
+            mSchemaMapLocked.clear();
+            mNamespaceMapLocked.clear();
+
+            // Must be called after everything else since VisibilityStore may repopulate
+            // IcingSearchEngine with an initial schema.
+            mVisibilityStoreLocked.handleReset();
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
         checkSuccess(resetResultProto.getStatus());
     }
 
+    /** Wrapper around schema changes */
+    @VisibleForTesting
+    static class RewrittenSchemaResults {
+        // Any database-qualified types that used to exist in the schema, but are deleted in the
+        // new one.
+        final Set<String> mDeletedQualifiedTypes = new ArraySet<>();
+
+        // Database-qualified types that were part of the new schema.
+        final Set<String> mRewrittenQualifiedTypes = new ArraySet<>();
+    }
+
     /**
      * Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
      * Rewritten types will be added to the {@code existingSchema}.
      *
-     * @param databaseName   The name of the database where this schema lives.
+     * @param databaseName The name of the database where this schema lives.
      * @param existingSchema A schema that may contain existing types from across all database
-     *                       instances. Will be mutated to contain the properly rewritten schema
-     *                       types from {@code newSchema}.
-     * @param newSchema      Schema with types to add to the {@code existingSchema}.
-     * @return a Set contains all remaining qualified schema type names in given database.
+     *     instances. Will be mutated to contain the properly rewritten schema types from {@code
+     *     newSchema}.
+     * @param newSchema Schema with types to add to the {@code existingSchema}.
+     * @return a RewrittenSchemaResults contains all qualified schema type names in the given
+     *     database as well as a set of schema types that were deleted from the database.
      */
     @VisibleForTesting
-    Set<String> rewriteSchema(@NonNull String databaseName,
+    static RewrittenSchemaResults rewriteSchema(
+            @NonNull String databaseName,
             @NonNull SchemaProto.Builder existingSchema,
-            @NonNull SchemaProto newSchema) throws AppSearchException {
+            @NonNull SchemaProto newSchema)
+            throws AppSearchException {
         String prefix = getDatabasePrefix(databaseName);
         HashMap<String, SchemaTypeConfigProto> newTypesToProto = new HashMap<>();
         // Rewrite the schema type to include the typePrefix.
@@ -523,8 +646,7 @@
                 PropertyConfigProto.Builder propertyConfigBuilder =
                         typeConfigBuilder.getProperties(propertyIdx).toBuilder();
                 if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
-                    String newPropertySchemaType =
-                            prefix + propertyConfigBuilder.getSchemaType();
+                    String newPropertySchemaType = prefix + propertyConfigBuilder.getSchemaType();
                     propertyConfigBuilder.setSchemaType(newPropertySchemaType);
                     typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
                 }
@@ -533,7 +655,9 @@
             newTypesToProto.put(newSchemaType, typeConfigBuilder.build());
         }
 
-        Set<String> newSchemaTypesName = newTypesToProto.keySet();
+        // newTypesToProto is modified below, so we need a copy first
+        RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
+        rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet());
 
         // Combine the existing schema (which may have types from other databases) with this
         // database's new schema. Modifies the existingSchemaBuilder.
@@ -548,26 +672,26 @@
                 // All types existing before but not in newSchema should be removed.
                 existingSchema.removeTypes(i);
                 --i;
+                rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType);
             }
         }
         // We've been removing existing types from newTypesToProto, so everything that remains is
         // new.
         existingSchema.addAllTypes(newTypesToProto.values());
 
-        return newSchemaTypesName;
+        return rewrittenSchemaResults;
     }
 
     /**
-     * Prepends {@code prefix} to all types and namespaces mentioned anywhere in
-     * {@code documentBuilder}.
+     * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
+     * documentBuilder}.
      *
      * @param documentBuilder The document to mutate
-     * @param prefix          The prefix to add
+     * @param prefix The prefix to add
      */
     @VisibleForTesting
-    void addPrefixToDocument(
-            @NonNull DocumentProto.Builder documentBuilder,
-            @NonNull String prefix) {
+    static void addPrefixToDocument(
+            @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
         // Rewrite the type name to include/remove the prefix.
         String newSchema = prefix + documentBuilder.getSchema();
         documentBuilder.setSchema(newSchema);
@@ -595,27 +719,17 @@
     }
 
     /**
-     * Removes any database names from types and namespaces mentioned anywhere in
-     * {@code documentBuilder}.
+     * Removes any database names from types and namespaces mentioned anywhere in {@code
+     * documentBuilder}.
      *
      * @param documentBuilder The document to mutate
      */
     @VisibleForTesting
-    void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) {
-        int delimiterIndex;
-        if ((delimiterIndex = documentBuilder.getSchema().indexOf(DATABASE_DELIMITER)) != -1) {
-            // Rewrite the type name to remove the prefix.
-            // Add 1 to include the char size of the DATABASE_DELIMITER
-            String newSchema = documentBuilder.getSchema().substring(delimiterIndex + 1);
-            documentBuilder.setSchema(newSchema);
-        }
-
-        if ((delimiterIndex = documentBuilder.getNamespace().indexOf(DATABASE_DELIMITER)) != -1) {
-            // Rewrite the namespace to remove the prefix.
-            // Add 1 to include the char size of the DATABASE_DELIMITER
-            String newNamespace = documentBuilder.getNamespace().substring(delimiterIndex + 1);
-            documentBuilder.setNamespace(newNamespace);
-        }
+    static void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
+            throws AppSearchException {
+        // Rewrite the type name and namespace to remove the prefix.
+        documentBuilder.setSchema(removeDatabasePrefix(documentBuilder.getSchema()));
+        documentBuilder.setNamespace(removeDatabasePrefix(documentBuilder.getNamespace()));
 
         // Recurse into derived documents
         for (int propertyIdx = 0;
@@ -639,8 +753,9 @@
     /**
      * Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}.
      *
-     * <p>If the searchSpec has empty filter lists, all existing databases from
-     * {@code databaseNames} will be added.
+     * <p>If the searchSpec has empty filter lists, all existing databases from {@code
+     * databaseNames} will be added.
+     *
      * <p>This method should be only called in query methods and get the READ lock to keep thread
      * safety.
      *
@@ -648,11 +763,11 @@
      */
     @VisibleForTesting
     @GuardedBy("mReadWriteLock")
-    boolean rewriteSearchSpecForDatabases(
+    boolean rewriteSearchSpecForDatabasesLocked(
             @NonNull SearchSpecProto.Builder searchSpecBuilder,
             @NonNull Set<String> databaseNames) {
         // Create a copy since retainAll() modifies the original set.
-        Set<String> existingDatabases = new HashSet<>(mNamespaceMap.keySet());
+        Set<String> existingDatabases = new ArraySet<>(mNamespaceMapLocked.keySet());
         existingDatabases.retainAll(databaseNames);
 
         if (existingDatabases.isEmpty()) {
@@ -669,29 +784,29 @@
 
         // Rewrite filters to include a database prefix.
         for (String databaseName : existingDatabases) {
-            Set<String> existingSchemaTypes = mSchemaMap.get(databaseName);
+            Set<String> existingSchemaTypes = mSchemaMapLocked.get(databaseName);
+            String databaseNamePrefix = getDatabasePrefix(databaseName);
             if (schemaTypeFilters.isEmpty()) {
                 // Include all schema types
                 searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes);
             } else {
                 // Qualify the given schema types
-                for (String schemaType : schemaTypeFilters) {
-                    String qualifiedType = getDatabasePrefix(databaseName) + schemaType;
+                for (int i = 0; i < schemaTypeFilters.size(); i++) {
+                    String qualifiedType = databaseNamePrefix + schemaTypeFilters.get(i);
                     if (existingSchemaTypes.contains(qualifiedType)) {
                         searchSpecBuilder.addSchemaTypeFilters(qualifiedType);
                     }
-
                 }
             }
 
-            Set<String> existingNamespaces = mNamespaceMap.get(databaseName);
+            Set<String> existingNamespaces = mNamespaceMapLocked.get(databaseName);
             if (namespaceFilters.isEmpty()) {
                 // Include all namespaces
                 searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
             } else {
                 // Qualify the given namespaces.
-                for (String namespace : namespaceFilters) {
-                    String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
+                for (int i = 0; i < namespaceFilters.size(); i++) {
+                    String qualifiedNamespace = databaseNamePrefix + namespaceFilters.get(i);
                     if (existingNamespaces.contains(qualifiedNamespace)) {
                         searchSpecBuilder.addNamespaceFilters(qualifiedNamespace);
                     }
@@ -703,35 +818,71 @@
     }
 
     @VisibleForTesting
-    SchemaProto getSchemaProto() throws AppSearchException {
-        GetSchemaResultProto schemaProto = mIcingSearchEngine.getSchema();
+    @GuardedBy("mReadWriteLock")
+    SchemaProto getSchemaProtoLocked() throws AppSearchException {
+        GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema();
         // TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not.
         // TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run
         checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
         return schemaProto.getSchema();
     }
 
+    /** Returns true if {@code databaseName} has a {@code schemaType} */
+    @GuardedBy("mReadWriteLock")
+    boolean hasSchemaTypeLocked(@NonNull String databaseName, @NonNull String schemaType) {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(schemaType);
+
+        Set<String> schemaTypes = mSchemaMapLocked.get(databaseName);
+        if (schemaTypes == null) {
+            return false;
+        }
+
+        return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType);
+    }
+
+    /** Returns a set of all databases AppSearchImpl knows about. */
+    @GuardedBy("mReadWriteLock")
     @NonNull
-    private String getDatabasePrefix(@NonNull String databaseName) {
+    Set<String> getDatabasesLocked() {
+        return mSchemaMapLocked.keySet();
+    }
+
+    @NonNull
+    private static String getDatabasePrefix(@NonNull String databaseName) {
         // TODO(b/170370381): Reconsider the way we separate database names for security reasons.
         return databaseName + DATABASE_DELIMITER;
     }
 
     @NonNull
-    private String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
+    private static String removeDatabasePrefix(@NonNull String prefixedString)
+            throws AppSearchException {
+        int delimiterIndex;
+        if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
+            // Add 1 to include the char size of the DATABASE_DELIMITER
+            return prefixedString.substring(delimiterIndex + 1);
+        }
+        throw new AppSearchException(
+                AppSearchResult.RESULT_UNKNOWN_ERROR,
+                "The prefixed value doesn't contains a valid database name.");
+    }
+
+    @NonNull
+    private static String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
         int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
         if (delimiterIndex == -1) {
-            throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
+            throw new AppSearchException(
+                    AppSearchResult.RESULT_UNKNOWN_ERROR,
                     "The databaseName prefixed value doesn't contains a valid database name.");
         }
         return prefixedValue.substring(0, delimiterIndex);
     }
 
-    @GuardedBy("mReadWriteLock")
-    private void addToMap(Map<String, Set<String>> map, String databaseName, String prefixedValue) {
+    private static void addToMap(
+            Map<String, Set<String>> map, String databaseName, String prefixedValue) {
         Set<String> values = map.get(databaseName);
         if (values == null) {
-            values = new HashSet<>();
+            values = new ArraySet<>();
             map.put(databaseName, values);
         }
         values.add(prefixedValue);
@@ -742,15 +893,15 @@
      *
      * @throws AppSearchException on error codes.
      */
-    private void checkSuccess(StatusProto statusProto) throws AppSearchException {
+    private static void checkSuccess(StatusProto statusProto) throws AppSearchException {
         checkCodeOneOf(statusProto, StatusProto.Code.OK);
     }
 
     /**
-     * Checks the given status code is one of the provided codes, and throws an
-     * {@link AppSearchException} if it is not.
+     * Checks the given status code is one of the provided codes, and throws an {@link
+     * AppSearchException} if it is not.
      */
-    private void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes)
+    private static void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes)
             throws AppSearchException {
         for (int i = 0; i < codes.length; i++) {
             if (codes[i] == statusProto.getCode()) {
@@ -774,27 +925,28 @@
      *
      * <p>This method should be only called in mutate methods and get the WRITE lock to keep thread
      * safety.
-     * <p>{@link IcingSearchEngine#optimize()} should be called only if
-     * {@link GetOptimizeInfoResultProto} shows there is enough resources could be released.
-     * <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per
-     * {@link #CHECK_OPTIMIZE_INTERVAL} of remove executions.
+     *
+     * <p>{@link IcingSearchEngine#optimize()} should be called only if {@link
+     * GetOptimizeInfoResultProto} shows there is enough resources could be released.
+     *
+     * <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per {@link
+     * #CHECK_OPTIMIZE_INTERVAL} of remove executions.
      *
      * @param force whether we should directly call {@link IcingSearchEngine#getOptimizeInfo()}.
      */
     @GuardedBy("mReadWriteLock")
-    private void checkForOptimize(boolean force) throws AppSearchException {
-        ++mOptimizeIntervalCount;
-        if (force || mOptimizeIntervalCount >= CHECK_OPTIMIZE_INTERVAL) {
-            mOptimizeIntervalCount = 0;
-            GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResult();
+    private void checkForOptimizeLocked(boolean force) throws AppSearchException {
+        ++mOptimizeIntervalCountLocked;
+        if (force || mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) {
+            mOptimizeIntervalCountLocked = 0;
+            GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked();
             checkSuccess(optimizeInfo.getStatus());
             // Second threshold, decide when to call optimize().
             if (optimizeInfo.getOptimizableDocs() >= OPTIMIZE_THRESHOLD_DOC_COUNT
-                    || optimizeInfo.getEstimatedOptimizableBytes()
-                    >= OPTIMIZE_THRESHOLD_BYTES) {
+                    || optimizeInfo.getEstimatedOptimizableBytes() >= OPTIMIZE_THRESHOLD_BYTES) {
                 // TODO(b/155939114): call optimize in the same thread will slow down api calls
                 //  significantly. Move this call to background.
-                OptimizeResultProto optimizeResultProto = mIcingSearchEngine.optimize();
+                OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
                 checkSuccess(optimizeResultProto.getStatus());
             }
             // TODO(b/147699081): Return OptimizeResultProto & log lost data detail once we add
@@ -804,8 +956,8 @@
     }
 
     /** Remove the rewritten schema types from any result documents. */
-    private SearchResultPage rewriteSearchResultProto(
-            @NonNull SearchResultProto searchResultProto) {
+    private static SearchResultPage rewriteSearchResultProto(
+            @NonNull SearchResultProto searchResultProto) throws AppSearchException {
         SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder();
         for (int i = 0; i < searchResultProto.getResultsCount(); i++) {
             if (searchResultProto.getResults(i).hasDocument()) {
@@ -820,40 +972,48 @@
         return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder);
     }
 
+    @GuardedBy("mReadWriteLock")
     @VisibleForTesting
-    GetOptimizeInfoResultProto getOptimizeInfoResult() {
-        return mIcingSearchEngine.getOptimizeInfo();
+    GetOptimizeInfoResultProto getOptimizeInfoResultLocked() {
+        return mIcingSearchEngineLocked.getOptimizeInfo();
+    }
+
+    @GuardedBy("mReadWriteLock")
+    @VisibleForTesting
+    VisibilityStore getVisibilityStoreLocked() {
+        return mVisibilityStoreLocked;
     }
 
     /**
-     * Converts an erroneous status code to an AppSearchException. Callers should ensure that
-     * the status code is not OK or WARNING_DATA_LOSS.
+     * Converts an erroneous status code to an AppSearchException. Callers should ensure that the
+     * status code is not OK or WARNING_DATA_LOSS.
      *
      * @param statusProto StatusProto with error code and message to translate into
-     *                    AppSearchException.
+     *     AppSearchException.
      * @return AppSearchException with the parallel error code.
      */
-    private AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
+    private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
         switch (statusProto.getCode()) {
             case INVALID_ARGUMENT:
-                return new AppSearchException(AppSearchResult.RESULT_INVALID_ARGUMENT,
-                        statusProto.getMessage());
+                return new AppSearchException(
+                        AppSearchResult.RESULT_INVALID_ARGUMENT, statusProto.getMessage());
             case NOT_FOUND:
-                return new AppSearchException(AppSearchResult.RESULT_NOT_FOUND,
-                        statusProto.getMessage());
+                return new AppSearchException(
+                        AppSearchResult.RESULT_NOT_FOUND, statusProto.getMessage());
             case FAILED_PRECONDITION:
                 // Fallthrough
             case ABORTED:
                 // Fallthrough
             case INTERNAL:
-                return new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
-                        statusProto.getMessage());
+                return new AppSearchException(
+                        AppSearchResult.RESULT_INTERNAL_ERROR, statusProto.getMessage());
             case OUT_OF_SPACE:
-                return new AppSearchException(AppSearchResult.RESULT_OUT_OF_SPACE,
-                        statusProto.getMessage());
+                return new AppSearchException(
+                        AppSearchResult.RESULT_OUT_OF_SPACE, statusProto.getMessage());
             default:
                 // Some unknown/unsupported error
-                return new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
+                return new AppSearchException(
+                        AppSearchResult.RESULT_UNKNOWN_ERROR,
                         "Unknown IcingSearchEngine status code: " + statusProto.getCode());
         }
     }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
new file mode 100644
index 0000000..4722822
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
@@ -0,0 +1,257 @@
+/*
+ * 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.localstorage;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists
+ * the visibility settings and reloads them on initialization.
+ *
+ * <p>The VisibilityStore creates a document for each database. This document holds the visibility
+ * settings that apply to that database. The VisibilityStore also creates a schema for these
+ * documents and has its own database so that its data doesn't interfere with any clients' data. It
+ * persists the document and schema through AppSearchImpl.
+ *
+ * <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings
+ * on who their data is visible to.
+ *
+ * <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
+ * higher level.
+ *
+ * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
+ * this class. Take care to not cause any circular dependencies.
+ */
+class VisibilityStore {
+    // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
+    @VisibleForTesting static final String SCHEMA_TYPE = "Visibility";
+    // Property that holds the list of platform-hidden schemas, as part of the visibility
+    // settings.
+    @VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
+    // Database name to prefix all visibility schemas and documents with. Special-cased to
+    // minimize the chance of collision with a client-supplied database.
+    @VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database";
+    // Namespace of documents that contain visibility settings
+    private static final String NAMESPACE = "namespace";
+    private final AppSearchImpl mAppSearchImpl;
+
+    // The map contains schemas that are platform-hidden for each database. All schemas in the map
+    // have a database name prefix.
+    private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>();
+
+    /**
+     * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
+     * before using the object.
+     *
+     * @param appSearchImpl AppSearchImpl instance
+     */
+    VisibilityStore(@NonNull AppSearchImpl appSearchImpl) {
+        mAppSearchImpl = appSearchImpl;
+    }
+
+    /**
+     * Initializes schemas and member variables to track visibility settings.
+     *
+     * <p>This is kept separate from the constructor because this will call methods on
+     * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
+     * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both
+     * AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
+     *
+     * @throws AppSearchException AppSearchException on AppSearchImpl error.
+     */
+    public void initialize() throws AppSearchException {
+        if (!mAppSearchImpl.hasSchemaTypeLocked(DATABASE_NAME, SCHEMA_TYPE)) {
+            // Schema type doesn't exist yet. Add it.
+            mAppSearchImpl.setSchema(
+                    DATABASE_NAME,
+                    Collections.singleton(
+                            new AppSearchSchema.Builder(SCHEMA_TYPE)
+                                    .addProperty(
+                                            new AppSearchSchema.PropertyConfig.Builder(
+                                                            PLATFORM_HIDDEN_PROPERTY)
+                                                    .setDataType(
+                                                            AppSearchSchema.PropertyConfig
+                                                                    .DATA_TYPE_STRING)
+                                                    .setCardinality(
+                                                            AppSearchSchema.PropertyConfig
+                                                                    .CARDINALITY_REPEATED)
+                                                    .build())
+                                    .build()),
+                    /*forceOverride=*/ false);
+        }
+
+        // Populate visibility settings map
+        for (String database : mAppSearchImpl.getDatabasesLocked()) {
+            if (database.equals(DATABASE_NAME)) {
+                // Our own database. Skip
+                continue;
+            }
+
+            try {
+                // Note: We use the other clients' database names as uris
+                GenericDocument document =
+                        mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database);
+
+                String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
+                mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
+            } catch (AppSearchException e) {
+                if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
+                    // TODO(b/172068212): This indicates some desync error. We were expecting a
+                    //  document, but didn't find one. Should probably reset AppSearch instead of
+                    //  ignoring it.
+                    continue;
+                }
+                // Otherwise, this is some other error we should pass up.
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Update visibility settings for the {@code databaseName}.
+     *
+     * @param schemasToRemove Database-prefixed schemas that should be removed
+     */
+    public void updateSchemas(@NonNull String databaseName, @NonNull Set<String> schemasToRemove)
+            throws AppSearchException {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(schemasToRemove);
+
+        GenericDocument visibilityDocument;
+        try {
+            visibilityDocument =
+                    mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
+        } catch (AppSearchException e) {
+            if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
+                // This might be the first time we're seeing visibility changes for a database.
+                // Create a new visibility document.
+                mAppSearchImpl.putDocument(
+                        DATABASE_NAME,
+                        new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
+                                .setNamespace(NAMESPACE)
+                                .build());
+
+                // Since we know there was nothing that existed before, we don't need to remove
+                // anything either. Return early.
+                return;
+            }
+            // Otherwise, this is some real error we should pass up.
+            throw e;
+        }
+
+        String[] hiddenSchemas =
+                visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
+        if (hiddenSchemas == null) {
+            // Nothing to remove.
+            return;
+        }
+
+        // Create a new set so we can remove from it.
+        Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas));
+        boolean changed = remainingSchemas.removeAll(schemasToRemove);
+        if (!changed) {
+            // Nothing was actually removed. Can return early.
+            return;
+        }
+
+        // Update our persisted document
+        // TODO(b/171882200): Switch to a .toBuilder API when it's available.
+        GenericDocument.Builder newVisibilityDocument =
+                new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
+                        .setNamespace(NAMESPACE);
+        if (!remainingSchemas.isEmpty()) {
+            newVisibilityDocument.setPropertyString(
+                    PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0]));
+        }
+        mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build());
+
+        // Update derived data structures
+        mPlatformHiddenMap.put(databaseName, remainingSchemas);
+    }
+
+    /**
+     * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
+     * overwritten.
+     *
+     * @param databaseName Database name that owns the {@code platformHiddenSchemas}.
+     * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the
+     *     platform.
+     * @throws AppSearchException on AppSearchImpl error.
+     */
+    public void setVisibility(
+            @NonNull String databaseName, @NonNull Set<String> platformHiddenSchemas)
+            throws AppSearchException {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(platformHiddenSchemas);
+
+        // Persist the document
+        GenericDocument.Builder visibilityDocument =
+                new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
+                        .setNamespace(NAMESPACE);
+        if (!platformHiddenSchemas.isEmpty()) {
+            visibilityDocument.setPropertyString(
+                    PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0]));
+        }
+        mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
+
+        // Update derived data structures.
+        mPlatformHiddenMap.put(databaseName, platformHiddenSchemas);
+    }
+
+    /**
+     * Returns the set of database-qualified schemas in {@code databaseName} that are hidden from
+     * the platform.
+     *
+     * @param databaseName Database name to retrieve schemas for
+     * @return Set of database-qualified schemas that are hidden from the platform. Empty set if
+     *     none exist.
+     */
+    @NonNull
+    public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) {
+        Preconditions.checkNotNull(databaseName);
+        Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName);
+        if (platformHiddenSchemas == null) {
+            return Collections.emptySet();
+        }
+        return platformHiddenSchemas;
+    }
+
+    /**
+     * Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a
+     * first-initialized state.
+     *
+     * @throws AppSearchException on AppSearchImpl error.
+     */
+    public void handleReset() throws AppSearchException {
+        mPlatformHiddenMap.clear();
+        initialize();
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index 60684f0..8f4e7ff6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -17,8 +17,8 @@
 package com.android.server.appsearch.external.localstorage.converter;
 
 import android.annotation.NonNull;
-
 import android.app.appsearch.GenericDocument;
+
 import com.android.internal.util.Preconditions;
 
 import com.google.android.icing.proto.DocumentProto;
@@ -30,9 +30,9 @@
 
 /**
  * Translates a {@link GenericDocument} into a {@link DocumentProto}.
+ *
  * @hide
  */
-
 public final class GenericDocumentToProtoConverter {
     private GenericDocumentToProtoConverter() {}
 
@@ -42,7 +42,8 @@
     public static DocumentProto convert(@NonNull GenericDocument document) {
         Preconditions.checkNotNull(document);
         DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
-        mProtoBuilder.setUri(document.getUri())
+        mProtoBuilder
+                .setUri(document.getUri())
                 .setSchema(document.getSchemaType())
                 .setNamespace(document.getNamespace())
                 .setScore(document.getScore())
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 403711f..642c2a7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -17,34 +17,34 @@
 package com.android.server.appsearch.external.localstorage.converter;
 
 import android.annotation.NonNull;
-
 import android.app.appsearch.AppSearchSchema;
+
 import com.android.internal.util.Preconditions;
 
-import com.google.android.icing.proto.IndexingConfig;
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.android.icing.proto.StringIndexingConfig;
 import com.google.android.icing.proto.TermMatchType;
 
 import java.util.List;
 
 /**
  * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
+ *
  * @hide
  */
-
 public final class SchemaToProtoConverter {
     private SchemaToProtoConverter() {}
 
     /**
-     * Converts an {@link android.app.appsearch.AppSearchSchema} into a
-     * {@link SchemaTypeConfigProto}.
+     * Converts an {@link android.app.appsearch.AppSearchSchema} into a {@link
+     * SchemaTypeConfigProto}.
      */
     @NonNull
     public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
         Preconditions.checkNotNull(schema);
         SchemaTypeConfigProto.Builder protoBuilder =
-                SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaTypeName());
+                SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType());
         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
             PropertyConfigProto propertyProto = convertProperty(properties.get(i));
@@ -57,9 +57,9 @@
     private static PropertyConfigProto convertProperty(
             @NonNull AppSearchSchema.PropertyConfig property) {
         Preconditions.checkNotNull(property);
-        PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder()
-                .setPropertyName(property.getName());
-        IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder();
+        PropertyConfigProto.Builder propertyConfigProto =
+                PropertyConfigProto.newBuilder().setPropertyName(property.getName());
+        StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
 
         // Set dataType
         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
@@ -104,17 +104,17 @@
         indexingConfig.setTermMatchType(termMatchTypeProto);
 
         // Set tokenizerType
-        @AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType =
-                property.getTokenizerType();
-        IndexingConfig.TokenizerType.Code tokenizerTypeProto =
-                IndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
+        @AppSearchSchema.PropertyConfig.TokenizerType
+        int tokenizerType = property.getTokenizerType();
+        StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
+                StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
         if (tokenizerTypeProto == null) {
             throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
         }
         indexingConfig.setTokenizerType(tokenizerTypeProto);
 
         // Build!
-        propertyConfigProto.setIndexingConfig(indexingConfig);
+        propertyConfigProto.setStringIndexingConfig(indexingConfig);
         return propertyConfigProto.build();
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index 4310b42..b91a393 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -16,13 +16,11 @@
 
 package com.android.server.appsearch.external.localstorage.converter;
 
-import android.os.Bundle;
-
 import android.annotation.NonNull;
-
 import android.app.appsearch.GenericDocument;
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResultPage;
+import android.os.Bundle;
 
 import com.google.android.icing.proto.SearchResultProto;
 import com.google.android.icing.proto.SearchResultProtoOrBuilder;
@@ -36,11 +34,9 @@
  *
  * @hide
  */
-
 public class SearchResultToProtoConverter {
     private SearchResultToProtoConverter() {}
 
-
     /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */
     @NonNull
     public static SearchResultPage convertToSearchResultPage(
@@ -68,8 +64,9 @@
             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());
+                    Bundle matchInfoBundle =
+                            convertToMatchInfoBundle(
+                                    entry.getSnippetMatches(j), entry.getPropertyName());
                     matchList.add(matchInfoBundle);
                 }
             }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index 14822dc..814ee4f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -17,8 +17,8 @@
 package com.android.server.appsearch.external.localstorage.converter;
 
 import android.annotation.NonNull;
-
 import android.app.appsearch.SearchSpec;
+
 import com.android.internal.util.Preconditions;
 
 import com.google.android.icing.proto.ResultSpecProto;
@@ -28,9 +28,9 @@
 
 /**
  * Translates a {@link SearchSpec} into icing search protos.
+ *
  * @hide
  */
-
 public final class SearchSpecToProtoConverter {
     private SearchSpecToProtoConverter() {}
 
@@ -38,9 +38,10 @@
     @NonNull
     public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
         Preconditions.checkNotNull(spec);
-        SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder()
-                .addAllSchemaTypeFilters(spec.getSchemas())
-                .addAllNamespaceFilters(spec.getNamespaces());
+        SearchSpecProto.Builder protoBuilder =
+                SearchSpecProto.newBuilder()
+                        .addAllSchemaTypeFilters(spec.getSchemaTypes())
+                        .addAllNamespaceFilters(spec.getNamespaces());
 
         @SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
         TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
@@ -57,7 +58,7 @@
     public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
         Preconditions.checkNotNull(spec);
         return ResultSpecProto.newBuilder()
-                .setNumPerPage(spec.getNumPerPage())
+                .setNumPerPage(spec.getResultCountPerPage())
                 .setSnippetSpec(
                         ResultSpecProto.SnippetSpecProto.newBuilder()
                                 .setNumToSnippet(spec.getSnippetCount())
@@ -84,8 +85,8 @@
         ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
                 ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
         if (rankingStrategyCodeProto == null) {
-            throw new IllegalArgumentException("Invalid result ranking strategy: "
-                    + rankingStrategyCode);
+            throw new IllegalArgumentException(
+                    "Invalid result ranking strategy: " + rankingStrategyCode);
         }
         protoBuilder.setRankBy(rankingStrategyCodeProto);
 
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
new file mode 100644
index 0000000..a2bf0d5
--- /dev/null
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -0,0 +1 @@
+I2decd83fab4c4d58fe38c9970f804046479c942c
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index d37dfde..f77f6c6 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -79,7 +79,6 @@
 import android.os.RemoteCallback;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -106,6 +105,7 @@
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
 import com.android.server.blob.BlobMetadata.Committer;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.StorageStatsManagerInternal;
 import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
 
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index c2d530d..2c8a558 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1506,56 +1506,16 @@
          * @return The job object to hand to the JobScheduler. This object is immutable.
          */
         public JobInfo build() {
-            // Check that network estimates require network type
-            if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
-                throw new IllegalArgumentException(
-                        "Can't provide estimated network usage without requiring a network");
-            }
-            // We can't serialize network specifiers
-            if (mIsPersisted && mNetworkRequest != null
-                    && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
-                throw new IllegalArgumentException(
-                        "Network specifiers aren't supported for persistent jobs");
-            }
-            // Check that a deadline was not set on a periodic job.
-            if (mIsPeriodic) {
-                if (mMaxExecutionDelayMillis != 0L) {
-                    throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
-                            "periodic job.");
-                }
-                if (mMinLatencyMillis != 0L) {
-                    throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
-                            "periodic job");
-                }
-                if (mTriggerContentUris != null) {
-                    throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
-                            "periodic job");
-                }
-            }
-            if (mIsPersisted) {
-                if (mTriggerContentUris != null) {
-                    throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
-                            "persisted job");
-                }
-                if (!mTransientExtras.isEmpty()) {
-                    throw new IllegalArgumentException("Can't call setTransientExtras() on a " +
-                            "persisted job");
-                }
-                if (mClipData != null) {
-                    throw new IllegalArgumentException("Can't call setClipData() on a " +
-                            "persisted job");
-                }
-            }
-            if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) {
-                throw new IllegalArgumentException("An important while foreground job cannot "
-                        + "have a time delay");
-            }
+            // This check doesn't need to be inside enforceValidity. It's an unnecessary legacy
+            // check that would ideally be phased out instead.
             if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
                 throw new IllegalArgumentException("An idle mode job will not respect any" +
                         " back-off policy, so calling setBackoffCriteria with" +
                         " setRequiresDeviceIdle is an error.");
             }
-            return new JobInfo(this);
+            JobInfo jobInfo = new JobInfo(this);
+            jobInfo.enforceValidity();
+            return jobInfo;
         }
 
         /**
@@ -1570,6 +1530,59 @@
     }
 
     /**
+     * @hide
+     */
+    public void enforceValidity() {
+        // Check that network estimates require network type
+        if ((networkDownloadBytes > 0 || networkUploadBytes > 0) && networkRequest == null) {
+            throw new IllegalArgumentException(
+                    "Can't provide estimated network usage without requiring a network");
+        }
+
+        // Check that a deadline was not set on a periodic job.
+        if (isPeriodic) {
+            if (maxExecutionDelayMillis != 0L) {
+                throw new IllegalArgumentException(
+                        "Can't call setOverrideDeadline() on a periodic job.");
+            }
+            if (minLatencyMillis != 0L) {
+                throw new IllegalArgumentException(
+                        "Can't call setMinimumLatency() on a periodic job");
+            }
+            if (triggerContentUris != null) {
+                throw new IllegalArgumentException(
+                        "Can't call addTriggerContentUri() on a periodic job");
+            }
+        }
+
+        if (isPersisted) {
+            // We can't serialize network specifiers
+            if (networkRequest != null
+                    && networkRequest.networkCapabilities.getNetworkSpecifier() != null) {
+                throw new IllegalArgumentException(
+                        "Network specifiers aren't supported for persistent jobs");
+            }
+            if (triggerContentUris != null) {
+                throw new IllegalArgumentException(
+                        "Can't call addTriggerContentUri() on a persisted job");
+            }
+            if (!transientExtras.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "Can't call setTransientExtras() on a persisted job");
+            }
+            if (clipData != null) {
+                throw new IllegalArgumentException(
+                        "Can't call setClipData() on a persisted job");
+            }
+        }
+
+        if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && hasEarlyConstraint) {
+            throw new IllegalArgumentException(
+                    "An important while foreground job cannot have a time delay");
+        }
+    }
+
+    /**
      * Convert a priority integer into a human readable string for debugging.
      * @hide
      */
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index ab94da8..3d43d20 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -17,7 +17,6 @@
 package android.app.job;
 
 import android.app.Service;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
@@ -26,8 +25,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-
 import java.lang.ref.WeakReference;
 
 /**
@@ -206,8 +203,7 @@
 
     /**
      * Call in to engine to report that a job has finished executing.  See
-     * {@link JobService#jobFinished(JobParameters, boolean)}  JobService.jobFinished} for more
-     * information.
+     * {@link JobService#jobFinished(JobParameters, boolean)} for more information.
      */
     public void jobFinished(JobParameters params, boolean needsReschedule) {
         if (params == null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 9835e18..9989252 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -884,13 +884,12 @@
         private static final String KEY_MAX_IDLE_TIMEOUT = "max_idle_to";
         private static final String KEY_IDLE_FACTOR = "idle_factor";
         private static final String KEY_MIN_TIME_TO_ALARM = "min_time_to_alarm";
-        // TODO(166121524): update flag names
-        private static final String KEY_MAX_TEMP_APP_WHITELIST_DURATION =
-                "max_temp_app_whitelist_duration";
-        private static final String KEY_MMS_TEMP_APP_WHITELIST_DURATION =
-                "mms_temp_app_whitelist_duration";
-        private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
-                "sms_temp_app_whitelist_duration";
+        private static final String KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS =
+                "max_temp_app_allowlist_duration_ms";
+        private static final String KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS =
+                "mms_temp_app_allowlist_duration_ms";
+        private static final String KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS =
+                "sms_temp_app_allowlist_duration_ms";
         private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION_MS =
                 "notification_allowlist_duration_ms";
         /**
@@ -949,9 +948,9 @@
         private static final float DEFAULT_IDLE_FACTOR = 2f;
         private static final long DEFAULT_MIN_TIME_TO_ALARM =
                 !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L;
-        private static final long DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION = 5 * 60 * 1000L;
-        private static final long DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION = 60 * 1000L;
-        private static final long DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION = 20 * 1000L;
+        private static final long DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = 5 * 60 * 1000L;
+        private static final long DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = 60 * 1000L;
+        private static final long DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = 20 * 1000L;
         private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L;
         private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
         private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
@@ -1142,21 +1141,21 @@
          * Max amount of time to temporarily whitelist an app when it receives a high priority
          * tickle.
          *
-         * @see #KEY_MAX_TEMP_APP_WHITELIST_DURATION
+         * @see #KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long MAX_TEMP_APP_WHITELIST_DURATION = DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION;
+        public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an MMS.
-         * @see #KEY_MMS_TEMP_APP_WHITELIST_DURATION
+         * @see #KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long MMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION;
+        public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an SMS.
-         * @see #KEY_SMS_TEMP_APP_WHITELIST_DURATION
+         * @see #KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long SMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION;
+        public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
 
         /**
          * Amount of time we would like to whitelist an app that is handling a
@@ -1303,20 +1302,20 @@
                             MIN_TIME_TO_ALARM = properties.getLong(
                                     KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM);
                             break;
-                        case KEY_MAX_TEMP_APP_WHITELIST_DURATION:
-                            MAX_TEMP_APP_WHITELIST_DURATION = properties.getLong(
-                                    KEY_MAX_TEMP_APP_WHITELIST_DURATION,
-                                    DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION);
+                        case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
+                            MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
+                                    KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
+                                    DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
                             break;
-                        case KEY_MMS_TEMP_APP_WHITELIST_DURATION:
-                            MMS_TEMP_APP_WHITELIST_DURATION = properties.getLong(
-                                    KEY_MMS_TEMP_APP_WHITELIST_DURATION,
-                                    DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION);
+                        case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
+                            MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
+                                    KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+                                    DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS);
                             break;
-                        case KEY_SMS_TEMP_APP_WHITELIST_DURATION:
-                            SMS_TEMP_APP_WHITELIST_DURATION = properties.getLong(
-                                    KEY_SMS_TEMP_APP_WHITELIST_DURATION,
-                                    DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION);
+                        case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
+                            SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
+                                    KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+                                    DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS);
                             break;
                         case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
                             NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
@@ -1438,16 +1437,16 @@
             TimeUtils.formatDuration(MIN_TIME_TO_ALARM, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_MAX_TEMP_APP_WHITELIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(MAX_TEMP_APP_WHITELIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("=");
+            TimeUtils.formatDuration(MAX_TEMP_APP_ALLOWLIST_DURATION_MS, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_MMS_TEMP_APP_WHITELIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(MMS_TEMP_APP_WHITELIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("=");
+            TimeUtils.formatDuration(MMS_TEMP_APP_ALLOWLIST_DURATION_MS, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_SMS_TEMP_APP_WHITELIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("=");
+            TimeUtils.formatDuration(SMS_TEMP_APP_ALLOWLIST_DURATION_MS, pw);
             pw.println();
 
             pw.print("    "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION_MS); pw.print("=");
@@ -1804,29 +1803,29 @@
         public long whitelistAppTemporarily(String packageName, int userId, String reason)
                 throws RemoteException {
             // At least 10 seconds.
-            long duration = Math.max(10_000L, mConstants.MAX_TEMP_APP_WHITELIST_DURATION / 2);
-            addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
-            return duration;
+            long durationMs = Math.max(10_000L, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS / 2);
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            return durationMs;
         }
 
         @Override
         public void addPowerSaveTempWhitelistApp(String packageName, long duration,
                 int userId, String reason) throws RemoteException {
-            addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
+            addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reason);
         }
 
         @Override public long addPowerSaveTempWhitelistAppForMms(String packageName,
                 int userId, String reason) throws RemoteException {
-            long duration = mConstants.MMS_TEMP_APP_WHITELIST_DURATION;
-            addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
-            return duration;
+            long durationMs = mConstants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            return durationMs;
         }
 
         @Override public long addPowerSaveTempWhitelistAppForSms(String packageName,
                 int userId, String reason) throws RemoteException {
-            long duration = mConstants.SMS_TEMP_APP_WHITELIST_DURATION;
-            addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
-            return duration;
+            long durationMs = mConstants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            return durationMs;
         }
 
         @Override public void exitIdle(String reason) {
@@ -1900,7 +1899,7 @@
         @Override
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
                 long duration, int userId, boolean sync, String reason) {
-            addPowerSaveTempWhitelistAppInternal(callingUid, packageName, duration,
+            addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
                     userId, sync, reason);
         }
 
@@ -2617,7 +2616,7 @@
         }
     }
 
-    void addPowerSaveTempWhitelistAppChecked(String packageName, long duration,
+    void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
             int userId, String reason) throws RemoteException {
         getContext().enforceCallingPermission(
                 Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
@@ -2632,14 +2631,14 @@
                 "addPowerSaveTempWhitelistApp", null);
         final long token = Binder.clearCallingIdentity();
         try {
-            addPowerSaveTempWhitelistAppInternal(callingUid,
+            addPowerSaveTempAllowlistAppInternal(callingUid,
                     packageName, duration, userId, true, reason);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    void removePowerSaveTempWhitelistAppChecked(String packageName, int userId)
+    void removePowerSaveTempAllowlistAppChecked(String packageName, int userId)
             throws RemoteException {
         getContext().enforceCallingPermission(
                 Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
@@ -2654,7 +2653,7 @@
                 "removePowerSaveTempWhitelistApp", null);
         final long token = Binder.clearCallingIdentity();
         try {
-            removePowerSaveTempWhitelistAppInternal(packageName, userId);
+            removePowerSaveTempAllowlistAppInternal(packageName, userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -2664,7 +2663,7 @@
      * Adds an app to the temporary whitelist and resets the endTime for granting the
      * app an exemption to access network and acquire wakelocks.
      */
-    void addPowerSaveTempWhitelistAppInternal(int callingUid, String packageName,
+    void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName,
             long duration, int userId, boolean sync, String reason) {
         try {
             int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
@@ -2690,7 +2689,7 @@
                             + " is not on whitelist");
                 }
             }
-            duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION);
+            duration = Math.min(duration, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
             Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId);
             final boolean newEntry = entry == null;
             // Set the new end time
@@ -2728,7 +2727,7 @@
     /**
      * Removes an app from the temporary whitelist and notifies the observers.
      */
-    private void removePowerSaveTempWhitelistAppInternal(String packageName, int userId) {
+    private void removePowerSaveTempAllowlistAppInternal(String packageName, int userId) {
         try {
             final int uid = getContext().getPackageManager().getPackageUidAsUser(
                     packageName, userId);
@@ -4354,9 +4353,9 @@
             if (arg != null) {
                 try {
                     if (removePkg) {
-                        removePowerSaveTempWhitelistAppChecked(arg, shell.userId);
+                        removePowerSaveTempAllowlistAppChecked(arg, shell.userId);
                     } else {
-                        addPowerSaveTempWhitelistAppChecked(arg, duration, shell.userId, "shell");
+                        addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, "shell");
                     }
                 } catch (Exception e) {
                     pw.println("Failed: " + e);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ca002ec..255e2f6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -63,7 +63,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.WorkSource;
 import android.provider.DeviceConfig;
 import android.text.format.DateUtils;
@@ -104,6 +103,7 @@
 import com.android.server.job.controllers.TimeController;
 import com.android.server.job.restrictions.JobRestriction;
 import com.android.server.job.restrictions.ThermalStatusRestriction;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 import com.android.server.utils.quota.Categorizer;
@@ -1054,6 +1054,7 @@
 
     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
             int userId, String tag) {
+        // Rate limit excessive schedule() calls.
         final String servicePkg = job.getService().getPackageName();
         if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
             // Only limit schedule calls for persisted jobs scheduled by the app itself.
@@ -1358,8 +1359,7 @@
                 for (int i=0; i<mActiveServices.size(); i++) {
                     JobServiceContext jsc = mActiveServices.get(i);
                     final JobStatus executing = jsc.getRunningJobLocked();
-                    if (executing != null
-                            && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
+                    if (executing != null && !executing.canRunInDoze()) {
                         jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
                                 "cancelled due to doze");
                     }
@@ -1411,7 +1411,7 @@
                 final JobServiceContext jsc = mActiveServices.get(i);
                 final JobStatus job = jsc.getRunningJobLocked();
                 if (job != null
-                        && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
+                        && !job.canRunInDoze()
                         && !job.dozeWhitelisted
                         && !job.uidActive) {
                     // We will report active if we have a job running and it is not an exception
@@ -2600,6 +2600,7 @@
         // job that runs one of the app's services, as well as verifying that the
         // named service properly requires the BIND_JOB_SERVICE permission
         private void enforceValidJobRequest(int uid, JobInfo job) {
+            job.enforceValidity();
             final PackageManager pm = getContext()
                     .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
                     .getPackageManager();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 1e72062..cc20213 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -20,10 +20,11 @@
 import android.app.AppGlobals;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.os.BasicShellCommandHandler;
 import android.os.Binder;
 import android.os.UserHandle;
 
+import com.android.modules.utils.BasicShellCommandHandler;
+
 import java.io.PrintWriter;
 
 public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 44c8fcf..f647db9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -189,8 +189,7 @@
         final String packageName = jobStatus.getSourcePackageName();
 
         final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
-                (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
-                        != 0);
+                jobStatus.canRunInBatterySaver());
 
         final boolean isActive;
         if (activeState == UNKNOWN) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 67997cf..6ddafad 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -464,7 +464,7 @@
             NetworkCapabilities capabilities) {
         // TODO: consider matching against non-active networks
 
-        final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+        final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking();
         final NetworkInfo info = mConnManager.getNetworkInfoForUid(network,
                 jobStatus.getSourceUid(), ignoreBlocked);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 00dbb82..8f6c68d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -470,7 +470,7 @@
         }
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
-        mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+        mReadyNotDozing = canRunInDoze();
         if (standbyBucket == RESTRICTED_INDEX) {
             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
         } else {
@@ -1036,6 +1036,22 @@
         mPersistedUtcTimes = null;
     }
 
+    /**
+     * @return true if the job is exempted from Doze restrictions and therefore allowed to run
+     * in Doze.
+     */
+    public boolean canRunInDoze() {
+        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+    }
+
+    boolean canRunInBatterySaver() {
+        return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0;
+    }
+
+    boolean shouldIgnoreNetworkBlocking() {
+        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+    }
+
     /** @return true if the constraint was changed, false otherwise. */
     boolean setChargingConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
@@ -1086,7 +1102,7 @@
         dozeWhitelisted = whitelisted;
         if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
             // The constraint was changed. Update the ready flag.
-            mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+            mReadyNotDozing = state || canRunInDoze();
             return true;
         }
         return false;
@@ -1771,9 +1787,11 @@
             pw.print(prefix); pw.print("  readyDeadlineSatisfied: ");
             pw.println(mReadyDeadlineSatisfied);
         }
-        pw.print(prefix);
-        pw.print("  readyDynamicSatisfied: ");
-        pw.println(mReadyDynamicSatisfied);
+        if (mDynamicConstraints != 0) {
+            pw.print(prefix);
+            pw.print("  readyDynamicSatisfied: ");
+            pw.println(mReadyDynamicSatisfied);
+        }
         pw.print(prefix);
         pw.print("  readyComponentEnabled: ");
         pw.println(serviceInfo != null);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 7d7de3b..1d72b42 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -1679,42 +1679,47 @@
             // Update job bookkeeping out of band.
             JobSchedulerBackgroundThread.getHandler().post(() -> {
                 final int bucketIndex = JobSchedulerService.standbyBucketToBucketIndex(bucket);
-                if (DEBUG) {
-                    Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex "
-                            + bucketIndex);
-                }
-                List<JobStatus> restrictedChanges = new ArrayList<>();
-                synchronized (mLock) {
-                    ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
-                    if (jobs == null || jobs.size() == 0) {
-                        return;
-                    }
-                    for (int i = jobs.size() - 1; i >= 0; i--) {
-                        JobStatus js = jobs.valueAt(i);
-                        // Effective standby bucket can change after this in some situations so
-                        // use the real bucket so that the job is tracked by the controllers.
-                        if ((bucketIndex == RESTRICTED_INDEX
-                                || js.getStandbyBucket() == RESTRICTED_INDEX)
-                                && bucketIndex != js.getStandbyBucket()) {
-                            restrictedChanges.add(js);
-                        }
-                        js.setStandbyBucket(bucketIndex);
-                    }
-                    Timer timer = mPkgTimers.get(userId, packageName);
-                    if (timer != null && timer.isActive()) {
-                        timer.rescheduleCutoff();
-                    }
-                    if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
-                        mStateChangedListener.onControllerStateChanged();
-                    }
-                }
-                if (restrictedChanges.size() > 0) {
-                    mStateChangedListener.onRestrictedBucketChanged(restrictedChanges);
-                }
+                updateStandbyBucket(userId, packageName, bucketIndex);
             });
         }
     }
 
+    @VisibleForTesting
+    void updateStandbyBucket(
+            final int userId, final @NonNull String packageName, final int bucketIndex) {
+        if (DEBUG) {
+            Slog.i(TAG, "Moving pkg " + string(userId, packageName)
+                    + " to bucketIndex " + bucketIndex);
+        }
+        List<JobStatus> restrictedChanges = new ArrayList<>();
+        synchronized (mLock) {
+            ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+            if (jobs == null || jobs.size() == 0) {
+                return;
+            }
+            for (int i = jobs.size() - 1; i >= 0; i--) {
+                JobStatus js = jobs.valueAt(i);
+                // Effective standby bucket can change after this in some situations so
+                // use the real bucket so that the job is tracked by the controllers.
+                if ((bucketIndex == RESTRICTED_INDEX || js.getStandbyBucket() == RESTRICTED_INDEX)
+                        && bucketIndex != js.getStandbyBucket()) {
+                    restrictedChanges.add(js);
+                }
+                js.setStandbyBucket(bucketIndex);
+            }
+            Timer timer = mPkgTimers.get(userId, packageName);
+            if (timer != null && timer.isActive()) {
+                timer.rescheduleCutoff();
+            }
+            if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+                mStateChangedListener.onControllerStateChanged();
+            }
+        }
+        if (restrictedChanges.size() > 0) {
+            mStateChangedListener.onRestrictedBucketChanged(restrictedChanges);
+        }
+    }
+
     private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> {
         private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() {
             public boolean test(TimingSession ts) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0f60eae..8363e9f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -42,6 +42,7 @@
 import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/service/usb.proto";
+import "frameworks/base/core/proto/android/stats/camera/camera.proto";
 import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto";
 import "frameworks/base/core/proto/android/stats/connectivity/tethering.proto";
 import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto";
@@ -60,6 +61,7 @@
 import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
 import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
 import "frameworks/base/core/proto/android/stats/tls/enums.proto";
+import "frameworks/base/core/proto/android/stats/tv/tif_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
@@ -510,6 +512,8 @@
         MediaPlaybackTrackChanged media_playback_track_changed = 324;
         WifiScanReported wifi_scan_reported = 325 [(module) = "wifi"];
         WifiPnoScanReported wifi_pno_scan_reported = 326  [(module) = "wifi"];
+        TifTuneStateChanged tif_tune_changed = 327 [(module) = "framework"];
+        AutoRotateReported auto_rotate_reported = 328 [(module) = "framework"];
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -3766,6 +3770,7 @@
         WARM = 1;
         HOT = 2;
         COLD = 3;
+        RELAUNCH = 4;
     }
     // The transition type.
     optional TransitionType type = 3;
@@ -3827,6 +3832,7 @@
         WARM = 1;
         HOT = 2;
         COLD = 3;
+        RELAUNCH = 4;
     }
     // The transition type.
     optional TransitionType type = 3;
@@ -9946,10 +9952,12 @@
  *   frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
  */
 message CameraActionEvent {
-    // Camera session duration
+    // Camera session duration in milliseconds if action is SESSION.
+    // 0 if action is OPEN or CLOSE.
     optional int64 duration_millis = 1;
 
-    // Camera API level used
+    // Camera API level used.
+    // 1 for camera1 API, and 2 for camera2 API.
     optional int32 api_level = 2;
 
     // Name of client package
@@ -9963,6 +9971,56 @@
         EXTERNAL = 3;
     }
     optional Facing facing = 4;
+
+    // Camera ID
+    optional string camera_id = 5;
+
+    // Camera action type
+    enum Action {
+        UNKNOWN_ACTION = 0;
+        OPEN = 1;
+        CLOSE = 2;
+        SESSION = 3;
+    }
+    optional Action action = 6;
+
+    // Whether the client is accessing camera using ndk
+    optional bool is_ndk = 7;
+
+    // Action OPEN: Open latency
+    // Action CLOSE: Close latency
+    // Action SESSION: Camera session creation duration.
+    //                 If this entry is reusing an existing session, the value is -1.
+    optional int32 latency_millis = 8;
+
+    // session type: 0 for normal mode, 1 for constrained high speed mode
+    optional int32 operating_mode = 9;
+
+    // If actioh is SESSION: number of internal reconfigurations
+    // Else: 0
+    optional int32 internal_reconfig = 10;
+
+    // Number of requests for this capture session. Only applicable to SESSION
+    // action.
+    optional int64 request_count = 11;
+    // Number of result errors. Only applicable to SESSION action.
+    optional int64 result_error_count = 12;
+    // Whether the device runs into error state.
+    optional bool device_error = 13;
+
+    // If action is SESSION: Stream states
+    // Else: stream_count = 0
+    optional int32 stream_count = 14;
+    optional android.stats.camera.CameraStreamProto stream_1 = 15
+    [(android.os.statsd.log_mode) = MODE_BYTES];
+    optional android.stats.camera.CameraStreamProto stream_2 = 16
+    [(android.os.statsd.log_mode) = MODE_BYTES];
+    optional android.stats.camera.CameraStreamProto stream_3 = 17
+    [(android.os.statsd.log_mode) = MODE_BYTES];
+    optional android.stats.camera.CameraStreamProto stream_4 = 18
+    [(android.os.statsd.log_mode) = MODE_BYTES];
+    optional android.stats.camera.CameraStreamProto stream_5 = 19
+    [(android.os.statsd.log_mode) = MODE_BYTES];
 }
 
 /**
@@ -10352,6 +10410,39 @@
 }
 
 /**
+ * Logs when a TV Input Service Session changes tune state
+ * This is atom ID 327.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
+ */
+message TifTuneStateChanged {
+
+    // Tv Input Service uid, TV Player uid
+    repeated AttributionNode attribution_node = 1 [
+        (state_field_option).primary_field_first_uid = true
+    ];
+    optional android.stats.tv.TifTuneState state = 2 [
+        (state_field_option).exclusive_state = true,
+        (state_field_option).default_state_value = 0,
+        (state_field_option).nested = false
+    ];
+    // This a globally unique 128 bit random number created by TvInputManagerService when
+    // android.media.tv.TvInputManager#createSession is called.
+    // It is has no device or user association.
+    // See android.media.tv.TvInputService.onCreateSession(java.lang.String, java.lang.String)
+    // WARNING: Any changes to this field should be carefully reviewed for privacy.
+    //          Inspect the code at
+    //          framework/base/cmds/statsd/src/atoms.proto
+    //               TifTuneState
+    //          frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
+    //              logTuneStateChanged
+    //              BinderService.createSession
+    //              SessionState.sessionId
+    optional string tif_session_id = 3 [(state_field_option).primary_field = true];
+}
+
+/**
  * Logs when a tune occurs through device's Frontend.
  * This is atom ID 276.
  *
@@ -12077,6 +12168,33 @@
 }
 
 /**
+ * Logs when an auto rotate event occurs while smart auto rotate is enabled.
+ */
+message AutoRotateReported {
+    enum Orientation {
+        UNKNOWN = 1;
+        ROTATION_0 = 2;
+        ROTATION_90 = 3;
+        ROTATION_180 = 4;
+        ROTATION_270 = 5;
+        DISABLED = 6;
+        UNAVAILABLE = 7;
+        FAILURE = 8;
+    }
+
+    // Orientation of the device when a rotation was detected.
+    optional Orientation current_orientation = 1;
+    // The orientation of the phone after rotation before going through the recommendation service.
+    optional Orientation proposed_orientation = 2;
+    // Orientation recommended by the smart autorotate service component outside of the platform. It
+    // may or may not match the proposed_orientation. Can be disabled or unavailable if the
+    // recommendation service is disabled or unavailable. Will be unknown if the service failed.
+    optional Orientation recommended_orientation = 3;
+    // Time taken to calculate the rotation recommendation.
+    optional int64 recommendation_process_duration_millis = 4;
+}
+
+/**
   * Pushes TLS handshake counters from Conscrypt.
   * Pulled from:
   *   external/conscrypt/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 822b40b..8d2f143 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -5575,7 +5575,6 @@
 android.os.BadParcelableException
 android.os.BaseBundle$NoImagePreloadHolder
 android.os.BaseBundle
-android.os.BasicShellCommandHandler
 android.os.BatteryManager
 android.os.BatteryManagerInternal
 android.os.BatteryProperty$1
@@ -6750,6 +6749,7 @@
 android.sysprop.-$$Lambda$TelephonyProperties$klELuV5zVSqFveC5l6c3FSJmLAU
 android.sysprop.-$$Lambda$TelephonyProperties$pFU8zg4eHAdooeRLJg1WBG52cKk
 android.sysprop.-$$Lambda$TelephonyProperties$sXc3eBCFirzHWb9pvClH7EsiM_Q
+android.stats.camera.nano.CameraStreamProto
 android.sysprop.AdbProperties
 android.sysprop.ApexProperties
 android.sysprop.ContactsProperties
@@ -11756,6 +11756,7 @@
 com.android.net.module.annotation.VisibleForTesting
 com.android.net.module.annotation.WorkerThread
 com.android.net.module.util.InetAddressUtils
+com.android.modules.utils.BasicShellCommandHandler
 com.android.okhttp.Address
 com.android.okhttp.AndroidShimResponseCache
 com.android.okhttp.Authenticator
diff --git a/core/api/current.txt b/core/api/current.txt
index f665512..9f7f391 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10158,6 +10158,7 @@
     method public abstract void grantUriPermission(String, android.net.Uri, int);
     method public abstract boolean isDeviceProtectedStorage();
     method public boolean isRestricted();
+    method public static boolean isUiContext(@NonNull android.content.Context);
     method public abstract boolean moveDatabaseFrom(android.content.Context, String);
     method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String);
     method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]);
@@ -12104,6 +12105,8 @@
     method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public abstract int getPreferredActivities(@NonNull java.util.List<android.content.IntentFilter>, @NonNull java.util.List<android.content.ComponentName>, @Nullable String);
     method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
+    method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12136,6 +12139,8 @@
     method public boolean isPackageSuspended();
     method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
     method public abstract boolean isSafeMode();
+    method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
+    method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
     method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
     method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
     method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int);
@@ -12144,6 +12149,9 @@
     method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
     method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
     method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
+    method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String);
+    method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String);
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
@@ -12332,7 +12340,7 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
-    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
     field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
@@ -12344,6 +12352,25 @@
     ctor public PackageManager.NameNotFoundException(String);
   }
 
+  public static final class PackageManager.Property implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean getBoolean();
+    method @Nullable public String getClassName();
+    method public float getFloat();
+    method public int getInteger();
+    method @NonNull public String getName();
+    method @NonNull public String getPackageName();
+    method public int getResourceId();
+    method @Nullable public String getString();
+    method public boolean isBoolean();
+    method public boolean isFloat();
+    method public boolean isInteger();
+    method public boolean isResourceId();
+    method public boolean isString();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.Property> CREATOR;
+  }
+
   @Deprecated public class PackageStats implements android.os.Parcelable {
     ctor @Deprecated public PackageStats(String);
     ctor @Deprecated public PackageStats(android.os.Parcel);
@@ -35793,6 +35820,7 @@
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot();
+    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
@@ -46190,12 +46218,6 @@
     field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
   }
 
-  public final class PhoneCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
-  }
-
   public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
     ctor public PhoneNumberFormattingTextWatcher();
     ctor public PhoneNumberFormattingTextWatcher(String);
@@ -46264,10 +46286,10 @@
 
   public class PhoneStateListener {
     ctor public PhoneStateListener();
-    ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
+    ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor);
     method public void onActiveDataSubscriptionIdChanged(int);
     method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
     method public void onCallForwardingIndicatorChanged(boolean);
     method public void onCallStateChanged(int, String);
     method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
@@ -46275,144 +46297,36 @@
     method public void onDataActivity(int);
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+    method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
     method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+    method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
     method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
     method public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method public void onUserMobileDataStateChanged(boolean);
-    field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
-    field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
-    field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20
-    field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400
-    field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10
-    field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
-    field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
-    field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
-    field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
-    field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
+    field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+    field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
+    field public static final int LISTEN_CALL_STATE = 32; // 0x20
+    field public static final int LISTEN_CELL_INFO = 1024; // 0x400
+    field public static final int LISTEN_CELL_LOCATION = 16; // 0x10
+    field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
+    field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
+    field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
+    field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
+    field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
-    field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+    field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
-    field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
-    field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
-  }
-
-  public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
-  }
-
-  public static interface PhoneStateListener.AlwaysReportedSignalStrengthsChangedListener {
-    method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
-  public static interface PhoneStateListener.BarringInfoChangedListener {
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
-  }
-
-  public static interface PhoneStateListener.CallDisconnectCauseChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
-  }
-
-  public static interface PhoneStateListener.CallForwardingIndicatorChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
-  }
-
-  public static interface PhoneStateListener.CallStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
-  }
-
-  public static interface PhoneStateListener.CarrierNetworkChangeListener {
-    method public void onCarrierNetworkChange(boolean);
-  }
-
-  public static interface PhoneStateListener.CellInfoChangedListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
-  }
-
-  public static interface PhoneStateListener.CellLocationChangedListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
-  }
-
-  public static interface PhoneStateListener.DataActivationStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.DataActivityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
-  }
-
-  public static interface PhoneStateListener.DataConnectionStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
-  }
-
-  public static interface PhoneStateListener.DisplayInfoChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
-  }
-
-  public static interface PhoneStateListener.EmergencyNumberListChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
-  }
-
-  public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-  }
-
-  public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
-  }
-
-  public static interface PhoneStateListener.PhoneCapabilityChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
-  }
-
-  public static interface PhoneStateListener.PhysicalChannelConfigChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
-  }
-
-  public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
-  }
-
-  public static interface PhoneStateListener.RegistrationFailedListener {
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
-  }
-
-  public static interface PhoneStateListener.ServiceStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
-  }
-
-  public static interface PhoneStateListener.SignalStrengthsChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
-  public static interface PhoneStateListener.UserMobileDataStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
-  }
-
-  public final class PhysicalChannelConfig implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getCellBandwidthDownlink();
-    method public int getChannelNumber();
-    method public int getConnectionStatus();
-    method public int getNetworkType();
-    method @IntRange(from=0, to=1007) public int getPhysicalCellId();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
-    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
-    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
-    field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
-    field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
+    field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
+    field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
   }
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
@@ -46595,6 +46509,8 @@
     field public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504; // 0x1f8
     field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f
     field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
+    field public static final int RESULT_RIL_ACCESS_BARRED = 122; // 0x7a
+    field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b
     field public static final int RESULT_RIL_CANCELLED = 119; // 0x77
     field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d
     field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71
@@ -46613,6 +46529,7 @@
     field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64
     field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72
     field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a
+    field public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; // 0x79
     field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78
     field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65
     field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c
@@ -46921,8 +46838,7 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
-    method @Deprecated public void listen(long, @NonNull android.telephony.PhoneStateListener);
-    method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener);
+    method public void listen(long, @NonNull android.telephony.PhoneStateListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
@@ -46944,7 +46860,6 @@
     method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
     method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
-    method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
     method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
     field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
@@ -52924,6 +52839,7 @@
     method public boolean performHapticFeedback(int, int);
     method public boolean performLongClick();
     method public boolean performLongClick(float, float);
+    method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
     method public void playSoundEffect(int);
     method public boolean post(Runnable);
     method public boolean postDelayed(Runnable, long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6cf93fc..0b044be 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2066,6 +2066,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
   }
 
+  public static class LauncherApps.ShortcutQuery {
+    field public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
+  }
+
   public class PackageInstaller {
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
     field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -4385,7 +4389,7 @@
   }
 
   public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
-    method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler);
   }
 
   public static interface MediaPlayer.OnRtpRxNoticeListener {
@@ -5027,14 +5031,15 @@
     method public void clearResourceLostListener();
     method public void close();
     method public int connectCiCam(int);
+    method public int connectFrontendToCiCam(int);
     method public int disconnectCiCam();
+    method public int disconnectFrontendToCiCam(int);
     method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
     method public long getAvSyncTime(int);
     method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
     method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getFrontendInfoList();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
-    method public int linkFrontendToCiCam(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
     method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
     method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -5048,7 +5053,6 @@
     method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
     method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
-    method public int unlinkFrontendToCiCam(int);
     method public void updateResourcePriority(int, int);
     field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
     field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
@@ -5249,7 +5253,7 @@
   public class Filter implements java.lang.AutoCloseable {
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
-    method public int configureScramblingStatusEvent(int);
+    method public int configureMonitorEvent(int);
     method public int flush();
     method public int getId();
     method public long getId64Bit();
@@ -5257,6 +5261,8 @@
     method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
     method public int start();
     method public int stop();
+    field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2
+    field public static final int MONITOR_EVENT_SCRAMBLING_STATUS = 1; // 0x1
     field public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED = 2; // 0x2
     field public static final int SCRAMBLING_STATUS_SCRAMBLED = 4; // 0x4
     field public static final int SCRAMBLING_STATUS_UNKNOWN = 1; // 0x1
@@ -5303,6 +5309,10 @@
     ctor public FilterEvent();
   }
 
+  public final class IpCidChangeEvent extends android.media.tv.tuner.filter.FilterEvent {
+    method public int getIpCid();
+  }
+
   public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
@@ -8953,11 +8963,8 @@
   public abstract class ImpressionAttestationService extends android.app.Service {
     ctor public ImpressionAttestationService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
-    method public abstract int onVerifyImpressionToken(@NonNull android.service.attestation.ImpressionToken);
-    field public static final int VERIFICATION_STATUS_APP_DECLARED = 2; // 0x2
-    field public static final int VERIFICATION_STATUS_OS_VERIFIED = 1; // 0x1
-    field public static final int VERIFICATION_STATUS_UNKNOWN = 0; // 0x0
+    method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull String, @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+    method public abstract boolean onVerifyImpressionToken(@NonNull String, @NonNull android.service.attestation.ImpressionToken);
   }
 
   public final class ImpressionToken implements android.os.Parcelable {
@@ -10203,6 +10210,25 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
   }
 
+  public final class DataThrottlingRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getCompletionDurationMillis();
+    method public int getDataThrottlingAction();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataThrottlingRequest> CREATOR;
+    field public static final int DATA_THROTTLING_ACTION_HOLD = 3; // 0x3
+    field public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0; // 0x0
+    field public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2; // 0x2
+    field public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1; // 0x1
+  }
+
+  public static final class DataThrottlingRequest.Builder {
+    ctor public DataThrottlingRequest.Builder();
+    method @NonNull public android.telephony.DataThrottlingRequest build();
+    method @NonNull public android.telephony.DataThrottlingRequest.Builder setCompletionDurationMillis(long);
+    method @NonNull public android.telephony.DataThrottlingRequest.Builder setDataThrottlingAction(int);
+  }
+
   public final class ImsiEncryptionInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getKeyIdentifier();
@@ -10340,83 +10366,35 @@
     method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+    method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
-    field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
-    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
-    field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
-    field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
-    field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
-    field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
-    field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
-    field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
-    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
-    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
-    field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
-    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
-    field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
-    field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
-    field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
-    field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
-  public static interface PhoneStateListener.CallAttributesChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-  }
-
-  public static interface PhoneStateListener.DataEnabledChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
-  }
-
-  public static interface PhoneStateListener.OutgoingEmergencyCallListener {
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
-  }
-
-  public static interface PhoneStateListener.OutgoingEmergencySmsListener {
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-  }
-
-  public static interface PhoneStateListener.PreciseCallStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-  }
-
-  public static interface PhoneStateListener.RadioPowerStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.SrvccStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.VoiceActivationStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
+  public final class PhysicalChannelConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCellBandwidthDownlink();
+    method public int getChannelNumber();
+    method public int getConnectionStatus();
+    method public int getNetworkType();
+    method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
+    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+    field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+    field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
   }
 
   public final class PinResult implements android.os.Parcelable {
@@ -10826,6 +10804,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method @Nullable public android.os.Bundle getVisualVoicemailSettings();
@@ -10869,6 +10848,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
@@ -10986,6 +10966,11 @@
     field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2
     field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff
     field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
+    field public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; // 0x3
+    field public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; // 0x1
+    field public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; // 0x2
+    field public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0; // 0x0
+    field public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4; // 0x4
   }
 
   public static interface TelephonyManager.CallForwardingInfoCallback {
@@ -11005,6 +10990,24 @@
     field public static final int ERROR_UNKNOWN = 0; // 0x0
   }
 
+  public final class ThermalMitigationRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest();
+    method public int getThermalMitigationAction();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ThermalMitigationRequest> CREATOR;
+    field public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0; // 0x0
+    field public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2; // 0x2
+    field public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1; // 0x1
+  }
+
+  public static final class ThermalMitigationRequest.Builder {
+    ctor public ThermalMitigationRequest.Builder();
+    method @NonNull public android.telephony.ThermalMitigationRequest build();
+    method @NonNull public android.telephony.ThermalMitigationRequest.Builder setDataThrottlingRequest(@NonNull android.telephony.DataThrottlingRequest);
+    method @NonNull public android.telephony.ThermalMitigationRequest.Builder setThermalMitigationAction(int);
+  }
+
   public final class UiccAccessRule implements android.os.Parcelable {
     ctor public UiccAccessRule(byte[], @Nullable String, long);
     method public int describeContents();
@@ -11082,6 +11085,35 @@
     field public static final String TYPE_XCAP_STRING = "xcap";
   }
 
+  public final class ApnThrottleStatus implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getApnType();
+    method public int getRetryType();
+    method public int getSlotIndex();
+    method public long getThrottleExpiryTimeMillis();
+    method public int getThrottleType();
+    method public int getTransportType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ApnThrottleStatus> CREATOR;
+    field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3
+    field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2
+    field public static final int RETRY_TYPE_NONE = 1; // 0x1
+    field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2
+    field public static final int THROTTLE_TYPE_NONE = 1; // 0x1
+  }
+
+  public static final class ApnThrottleStatus.Builder {
+    ctor public ApnThrottleStatus.Builder();
+    method @NonNull public android.telephony.data.ApnThrottleStatus build();
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setApnType(int);
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setNoThrottle();
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setRetryType(int);
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int);
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long);
+    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int);
+    field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL
+  }
+
   public final class DataCallResponse implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -11199,6 +11231,7 @@
     method public abstract void close();
     method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback);
     method public final int getSlotIndex();
+    method public final void notifyApnUnthrottled(@NonNull String);
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
     method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -11209,6 +11242,7 @@
   }
 
   public class DataServiceCallback {
+    method public void onApnUnthrottled(@NonNull String);
     method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
     method public void onDeactivateDataCallComplete(int);
     method public void onHandoverCancelled(int);
@@ -11234,6 +11268,7 @@
     ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
     method public abstract void close();
     method public final int getSlotIndex();
+    method public void reportApnThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ApnThrottleStatus>);
     method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f695adf..52a79ba 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -302,8 +302,10 @@
     method public void destroy();
     method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String);
     method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
+    method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
     method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
     method public void syncInputTransactions();
+    method public void syncInputTransactions(boolean);
   }
 
   public class UiModeManager {
@@ -1227,7 +1229,6 @@
   }
 
   public static final class StrictMode.VmPolicy.Builder {
-    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
   }
 
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index b1b9f41..b15fa27 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -650,7 +650,6 @@
                     0);
             flags = asAttributes.getInt(
                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
-            flags |= FLAG_REQUEST_2_FINGER_PASSTHROUGH;
             mSettingsActivityName = asAttributes.getString(
                     com.android.internal.R.styleable.AccessibilityService_settingsActivity);
             if (asAttributes.getBoolean(com.android.internal.R.styleable
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0015bc6..5537edb 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -78,6 +78,7 @@
 import android.util.Singleton;
 import android.util.Size;
 import android.view.Surface;
+import android.view.WindowInsetsController.Appearance;
 
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
@@ -2116,7 +2117,7 @@
         // the task having a secure window or having previews disabled
         private final boolean mIsRealSnapshot;
         private final int mWindowingMode;
-        private final int mSystemUiVisibility;
+        private final @Appearance int mAppearance;
         private final boolean mIsTranslucent;
         // Must be one of the named color spaces, otherwise, always use SRGB color space.
         private final ColorSpace mColorSpace;
@@ -2125,7 +2126,7 @@
                 @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
                 @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
                 Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
-                int windowingMode, int systemUiVisibility, boolean isTranslucent) {
+                int windowingMode, @Appearance int appearance, boolean isTranslucent) {
             mId = id;
             mTopActivityComponent = topActivityComponent;
             mSnapshot = snapshot;
@@ -2138,7 +2139,7 @@
             mIsLowResolution = isLowResolution;
             mIsRealSnapshot = isRealSnapshot;
             mWindowingMode = windowingMode;
-            mSystemUiVisibility = systemUiVisibility;
+            mAppearance = appearance;
             mIsTranslucent = isTranslucent;
         }
 
@@ -2157,7 +2158,7 @@
             mIsLowResolution = source.readBoolean();
             mIsRealSnapshot = source.readBoolean();
             mWindowingMode = source.readInt();
-            mSystemUiVisibility = source.readInt();
+            mAppearance = source.readInt();
             mIsTranslucent = source.readBoolean();
         }
 
@@ -2265,11 +2266,11 @@
         }
 
         /**
-         * @return The system ui visibility flags for the top most visible fullscreen window at the
+         * @return The {@link Appearance} flags for the top most visible fullscreen window at the
          *         time that the snapshot was taken.
          */
-        public int getSystemUiVisibility() {
-            return mSystemUiVisibility;
+        public @Appearance int getAppearance() {
+            return mAppearance;
         }
 
         @Override
@@ -2291,7 +2292,7 @@
             dest.writeBoolean(mIsLowResolution);
             dest.writeBoolean(mIsRealSnapshot);
             dest.writeInt(mWindowingMode);
-            dest.writeInt(mSystemUiVisibility);
+            dest.writeInt(mAppearance);
             dest.writeBoolean(mIsTranslucent);
         }
 
@@ -2311,7 +2312,7 @@
                     + " mIsLowResolution=" + mIsLowResolution
                     + " mIsRealSnapshot=" + mIsRealSnapshot
                     + " mWindowingMode=" + mWindowingMode
-                    + " mSystemUiVisibility=" + mSystemUiVisibility
+                    + " mAppearance=" + mAppearance
                     + " mIsTranslucent=" + mIsTranslucent;
         }
 
@@ -2336,7 +2337,7 @@
             private Rect mContentInsets;
             private boolean mIsRealSnapshot;
             private int mWindowingMode;
-            private int mSystemUiVisibility;
+            private @Appearance int mAppearance;
             private boolean mIsTranslucent;
             private int mPixelFormat;
 
@@ -2393,8 +2394,8 @@
                 return this;
             }
 
-            public Builder setSystemUiVisibility(int systemUiVisibility) {
-                mSystemUiVisibility = systemUiVisibility;
+            public Builder setAppearance(@Appearance int appearance) {
+                mAppearance = appearance;
                 return this;
             }
 
@@ -2428,7 +2429,7 @@
                         false /* isLowResolution */,
                         mIsRealSnapshot,
                         mWindowingMode,
-                        mSystemUiVisibility,
+                        mAppearance,
                         mIsTranslucent);
 
             }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d9f34d8..cdfe41e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -34,6 +34,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.database.DatabaseUtils;
@@ -7617,7 +7618,7 @@
                         // Only collect app-ops when the proxy is trusted
                         && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
                         myUid) == PackageManager.PERMISSION_GRANTED
-                        || isTrustedVoiceServiceProxy(mContext.getOpPackageName(), op))) {
+                        || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), op))) {
                     collectNotedOpSync(op, proxiedAttributionTag);
                 }
             }
@@ -7628,30 +7629,43 @@
         }
     }
 
-    private boolean isTrustedVoiceServiceProxy(String packageName, int code) {
+    /**
+     * Checks if the voice recognition service is a trust proxy.
+     *
+     * @return {@code true} if the package is a trust voice recognition service proxy
+     * @hide
+     */
+    public static boolean isTrustedVoiceServiceProxy(Context context, String packageName,
+            int code) {
         // This is a workaround for R QPR, new API change is not allowed. We only allow the current
         // voice recognizer is also the voice interactor to noteproxy op.
         if (code != OP_RECORD_AUDIO) {
             return false;
         }
         final String voiceRecognitionComponent = Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
-        final String voiceInteractionComponent = Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE);
+                context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
 
         final String voiceRecognitionServicePackageName =
                 getComponentPackageNameFromString(voiceRecognitionComponent);
-        final String voiceInteractionServicePackageName =
-                getComponentPackageNameFromString(voiceInteractionComponent);
-        return Objects.equals(packageName, voiceRecognitionServicePackageName) && Objects.equals(
-                voiceRecognitionServicePackageName, voiceInteractionServicePackageName);
+        return (Objects.equals(packageName, voiceRecognitionServicePackageName))
+                && isPackagePreInstalled(context, packageName);
     }
 
-    private String getComponentPackageNameFromString(String from) {
+    private static String getComponentPackageNameFromString(String from) {
         ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
         return componentName != null ? componentName.getPackageName() : "";
     }
 
+    private static boolean isPackagePreInstalled(Context context, String packageName) {
+        try {
+            final PackageManager pm = context.getPackageManager();
+            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Do a quick check for whether an application might be able to perform an operation.
      * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7cef93f..34437af 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -58,6 +58,8 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -3551,4 +3553,112 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public Property getProperty(String propertyName, String packageName)
+            throws NameNotFoundException {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(propertyName);
+        try {
+            final Property property = mPM.getProperty(propertyName, packageName, null);
+            if (property == null) {
+                throw new NameNotFoundException();
+            }
+            return property;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public Property getProperty(String propertyName, ComponentName component)
+            throws NameNotFoundException {
+        Objects.requireNonNull(component);
+        Objects.requireNonNull(propertyName);
+        try {
+            final Property property = mPM.getProperty(
+                    propertyName, component.getPackageName(), component.getClassName());
+            if (property == null) {
+                throw new NameNotFoundException();
+            }
+            return property;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public List<Property> queryApplicationProperty(String propertyName) {
+        Objects.requireNonNull(propertyName);
+        try {
+            final ParceledListSlice<Property> parceledList =
+                    mPM.queryProperty(propertyName, TYPE_APPLICATION);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public List<Property> queryActivityProperty(String propertyName) {
+        Objects.requireNonNull(propertyName);
+        try {
+            final ParceledListSlice<Property> parceledList =
+                    mPM.queryProperty(propertyName, TYPE_ACTIVITY);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public List<Property> queryProviderProperty(String propertyName) {
+        Objects.requireNonNull(propertyName);
+        try {
+            final ParceledListSlice<Property> parceledList =
+                    mPM.queryProperty(propertyName, TYPE_PROVIDER);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public List<Property> queryReceiverProperty(String propertyName) {
+        Objects.requireNonNull(propertyName);
+        try {
+            final ParceledListSlice<Property> parceledList =
+                    mPM.queryProperty(propertyName, TYPE_RECEIVER);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public List<Property> queryServiceProperty(String propertyName) {
+        Objects.requireNonNull(propertyName);
+        try {
+            final ParceledListSlice<Property> parceledList =
+                    mPM.queryProperty(propertyName, TYPE_SERVICE);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 9eeb9f6..ec7d783 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -36,8 +36,8 @@
 interface IUiAutomationConnection {
     void connect(IAccessibilityServiceClient client, int flags);
     void disconnect();
-    boolean injectInputEvent(in InputEvent event, boolean sync);
-    void syncInputTransactions();
+    boolean injectInputEvent(in InputEvent event, boolean sync, boolean waitForAnimations);
+    void syncInputTransactions(boolean waitForAnimations);
     boolean setRotation(int rotation);
     Bitmap takeScreenshot(in Rect crop);
     boolean clearWindowContentFrameStats(int windowId);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 3e249bb..e9d63d2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1119,7 +1119,8 @@
         }
         try {
             WindowManagerGlobal.getWindowManagerService().injectInputAfterTransactionsApplied(event,
-                    InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+                    InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH,
+                    true /* waitForAnimations */);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75f7cec..2bf5368 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -67,6 +67,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.BidiFormatter;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -4887,12 +4888,6 @@
             mN.mUsesStandardHeader = false;
         }
 
-        private RemoteViews applyStandardTemplate(int resId, int viewType,
-                TemplateBindResult result) {
-            return applyStandardTemplate(resId,
-                    mParams.reset().viewType(viewType).fillTextsFrom(this), result);
-        }
-
         private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
                 TemplateBindResult result) {
             p.headerless(resId == getBaseLayoutResource()
@@ -4906,7 +4901,7 @@
             bindNotificationHeader(contentView, p);
             bindLargeIconAndApplyMargin(contentView, p, result);
             boolean showProgress = handleProgressBar(contentView, ex, p);
-            if (p.title != null && p.title.length() > 0) {
+            if (p.title != null && p.title.length() > 0 && !p.mHasCustomContent) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
                 setTextViewColorPrimary(contentView, R.id.title, p);
@@ -5301,6 +5296,12 @@
                 contentView.setViewVisibility(R.id.app_name_text, View.GONE);
                 return false;
             }
+            if (p.mHeaderless && !p.mHasCustomContent) {
+                contentView.setViewVisibility(R.id.app_name_text, View.GONE);
+                // the headerless template will have the TITLE in this position; return true to
+                // keep the divider visible between that title and the next text element.
+                return true;
+            }
             contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE);
             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
             if (isColorized(p)) {
@@ -5348,14 +5349,11 @@
             big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
             big.setTextViewText(R.id.notification_material_reply_text_3, null);
 
+            final boolean snoozeEnabled = mContext.getContentResolver() != null
+                    && (Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
             big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
-                    R.dimen.notification_content_margin);
-        }
-
-        private RemoteViews applyStandardTemplateWithActions(int layoutId, int viewType,
-                TemplateBindResult result) {
-            return applyStandardTemplateWithActions(layoutId,
-                    mParams.reset().viewType(viewType).fillTextsFrom(this), result);
+                    snoozeEnabled ? 0 : R.dimen.notification_content_margin);
         }
 
         private static List<Notification.Action> filterOutContextualActions(
@@ -5495,8 +5493,10 @@
                     return styleView;
                 }
             }
-            return applyStandardTemplate(getBaseLayoutResource(),
-                    StandardTemplateParams.VIEW_TYPE_NORMAL, null /* result */);
+            StandardTemplateParams p = mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
+                    .fillTextsFrom(this);
+            return applyStandardTemplate(getBaseLayoutResource(), p, null /* result */);
         }
 
         private boolean useExistingRemoteView() {
@@ -5516,14 +5516,27 @@
                 result = mStyle.makeBigContentView();
                 hideLine1Text(result);
             }
-            if (result == null) {
-                result = applyStandardTemplateWithActions(getBigBaseLayoutResource(),
-                        StandardTemplateParams.VIEW_TYPE_BIG, null /* result */);
+            if (result == null && bigContentViewRequired()) {
+                StandardTemplateParams p = mParams.reset()
+                        .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+                        .fillTextsFrom(this);
+                result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p,
+                        null /* result */);
             }
             makeHeaderExpanded(result);
             return result;
         }
 
+        private boolean bigContentViewRequired() {
+            // If the big content view has no content, we can exempt the app from having to show it.
+            // TODO(b/173550917): add an UNDO style then force this requirement on apps targeting S
+            boolean exempt = mN.contentView != null && mN.bigContentView == null
+                    && mStyle == null && mActions.size() == 0
+                    && mN.extras.getCharSequence(EXTRA_TITLE) == null
+                    && mN.extras.getCharSequence(EXTRA_TEXT) == null;
+            return !exempt;
+        }
+
         /**
          * Construct a RemoteViews for the final notification header only. This will not be
          * colorized.
@@ -8689,18 +8702,24 @@
                return makeStandardTemplateWithCustomContent(headsUpContentView);
             }
             TemplateBindResult result = new TemplateBindResult();
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+                    .hasCustomContent(headsUpContentView != null)
+                    .fillTextsFrom(mBuilder);
             RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
-                    mBuilder.getHeadsUpBaseLayoutResource(),
-                    StandardTemplateParams.VIEW_TYPE_HEADS_UP, result);
+                    mBuilder.getHeadsUpBaseLayoutResource(), p, result);
             buildIntoRemoteViewContent(remoteViews, headsUpContentView, result, true);
             return remoteViews;
         }
 
         private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
             TemplateBindResult result = new TemplateBindResult();
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
+                    .hasCustomContent(customContent != null)
+                    .fillTextsFrom(mBuilder);
             RemoteViews remoteViews = mBuilder.applyStandardTemplate(
-                    mBuilder.getBaseLayoutResource(),
-                    StandardTemplateParams.VIEW_TYPE_NORMAL, result);
+                    mBuilder.getBaseLayoutResource(), p, result);
             buildIntoRemoteViewContent(remoteViews, customContent, result, true);
             return remoteViews;
         }
@@ -8710,9 +8729,12 @@
                     ? mBuilder.mN.contentView
                     : mBuilder.mN.bigContentView;
             TemplateBindResult result = new TemplateBindResult();
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+                    .hasCustomContent(bigContentView != null)
+                    .fillTextsFrom(mBuilder);
             RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
-                    mBuilder.getBigBaseLayoutResource(),
-                    StandardTemplateParams.VIEW_TYPE_BIG, result);
+                    mBuilder.getBigBaseLayoutResource(), p, result);
             buildIntoRemoteViewContent(remoteViews, bigContentView, result, false);
             return remoteViews;
         }
@@ -11025,6 +11047,7 @@
 
         int mViewType = VIEW_TYPE_UNSPECIFIED;
         boolean mHeaderless;
+        boolean mHasCustomContent;
         boolean hasProgress = true;
         CharSequence title;
         CharSequence text;
@@ -11038,6 +11061,7 @@
         final StandardTemplateParams reset() {
             mViewType = VIEW_TYPE_UNSPECIFIED;
             mHeaderless = false;
+            mHasCustomContent = false;
             hasProgress = true;
             title = null;
             text = null;
@@ -11064,6 +11088,11 @@
             return this;
         }
 
+        final StandardTemplateParams hasCustomContent(boolean hasCustomContent) {
+            this.mHasCustomContent = hasCustomContent;
+            return this;
+        }
+
         final StandardTemplateParams title(CharSequence title) {
             this.title = title;
             return this;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f76a757..9dbf1ff6 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -179,6 +179,12 @@
      * extras change, and don't care that any entities that received your
      * previous PendingIntent will be able to launch it with your new
      * extras even if they are not explicitly given to it.
+     *
+     * <p>{@link #FLAG_UPDATE_CURRENT} still works even if {@link
+     * #FLAG_IMMUTABLE} is set - the creator of the PendingIntent can always
+     * update the PendingIntent itself. The IMMUTABLE flag only limits the
+     * ability to alter the semantics of the intent that is sent by {@link
+     * #send} by the invoker of {@link #send}.
      */
     public static final int FLAG_UPDATE_CURRENT = 1<<27;
 
@@ -187,6 +193,11 @@
      * This means that the additional intent argument passed to the send
      * methods to fill in unpopulated properties of this intent will be
      * ignored.
+     *
+     * <p>{@link #FLAG_IMMUTABLE} only limits the ability to alter the
+     * semantics of the intent that is sent by {@link #send} by the invoker of
+     * {@link #send}. The creator of the PendingIntent can always update the
+     * PendingIntent itself via {@link #FLAG_UPDATE_CURRENT}.
      */
     public static final int FLAG_IMMUTABLE = 1<<26;
 
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index bc1bcbc..1ebf565 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -19,7 +19,7 @@
 import android.annotation.Nullable;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.SharedPreferences;
 import android.os.Build;
@@ -71,7 +71,7 @@
      * {@link android.content.SharedPreferences.Editor#clear Editor.clear}.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R)
     private static final long CALLBACK_ON_CLEAR_CHANGE = 119147584L;
 
     // Lock ordering rules:
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index b0307d1..d2be8a4 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -33,10 +33,10 @@
         },
         {
             "file_patterns": ["(/|^)AppOpsManager.java"],
-            "name": "CtsStatsdHostTestCases",
+            "name": "CtsStatsdAtomHostTestCases",
             "options": [
                 {
-                    "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps"
+                    "include-filter": "android.cts.statsdatom.appops.AppOpsTests#testAppOps"
                 }
             ]
         },
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 7fdf06f7..ca67dba 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,6 +36,7 @@
 import android.window.WindowContainerToken;
 
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Stores information about a particular Task.
@@ -220,6 +221,12 @@
      */
     public Rect parentBounds;
 
+    /**
+     * Whether this task is focused.
+     * @hide
+     */
+    public boolean isFocused;
+
     TaskInfo() {
         // Do nothing
     }
@@ -288,6 +295,37 @@
     }
 
     /**
+      * Returns {@code true} if parameters that are important for task organizers have changed
+      * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners
+      * about that.
+      * @hide
+      */
+    public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) {
+        if (that == null) {
+            return false;
+        }
+        return topActivityType == that.topActivityType
+                && isResizeable == that.isResizeable
+                && Objects.equals(positionInParent, that.positionInParent)
+                && equalsLetterboxParams(that)
+                && pictureInPictureParams == that.pictureInPictureParams
+                && getWindowingMode() == that.getWindowingMode()
+                && Objects.equals(taskDescription, that.taskDescription)
+                && isFocused == that.isFocused;
+    }
+
+    private boolean equalsLetterboxParams(TaskInfo that) {
+        return Objects.equals(letterboxActivityBounds, that.letterboxActivityBounds)
+                && Objects.equals(
+                        getConfiguration().windowConfiguration.getBounds(),
+                        that.getConfiguration().windowConfiguration.getBounds())
+                && Objects.equals(
+                        getConfiguration().windowConfiguration.getMaxBounds(),
+                        that.getConfiguration().windowConfiguration.getMaxBounds())
+                && Objects.equals(parentBounds, that.parentBounds);
+    }
+
+    /**
      * Reads the TaskInfo from a parcel.
      */
     void readFromParcel(Parcel source) {
@@ -319,6 +357,7 @@
         positionInParent = source.readTypedObject(Point.CREATOR);
         parentTaskId = source.readInt();
         parentBounds = source.readTypedObject(Rect.CREATOR);
+        isFocused = source.readBoolean();
     }
 
     /**
@@ -354,6 +393,7 @@
         dest.writeTypedObject(positionInParent, flags);
         dest.writeInt(parentTaskId);
         dest.writeTypedObject(parentBounds, flags);
+        dest.writeBoolean(isFocused);
     }
 
     @Override
@@ -376,8 +416,9 @@
                 + " launchCookies" + launchCookies
                 + " letterboxActivityBounds=" + letterboxActivityBounds
                 + " positionInParent=" + positionInParent
-                + " parentTaskId: " + parentTaskId
-                + " parentBounds: " + parentBounds
+                + " parentTaskId=" + parentTaskId
+                + " parentBounds=" + parentBounds
+                + " isFocused=" + isFocused
                 + "}";
     }
 }
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 1b0fd9e..787393e 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -695,6 +695,9 @@
 
     /**
      * A method for injecting an arbitrary input event.
+     *
+     * This method waits for all window container animations and surface operations to complete.
+     *
      * <p>
      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
      * </p>
@@ -704,12 +707,34 @@
      * @return Whether event injection succeeded.
      */
     public boolean injectInputEvent(InputEvent event, boolean sync) {
+        return injectInputEvent(event, sync, true /* waitForAnimations */);
+    }
+
+    /**
+     * A method for injecting an arbitrary input event, optionally waiting for window animations to
+     * complete.
+     * <p>
+     * <strong>Note:</strong> It is caller's responsibility to recycle the event.
+     * </p>
+     *
+     * @param event The event to inject.
+     * @param sync  Whether to inject the event synchronously.
+     * @param waitForAnimations Whether to wait for all window container animations and surface
+     *   operations to complete.
+     * @return Whether event injection succeeded.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean injectInputEvent(@NonNull InputEvent event, boolean sync,
+            boolean waitForAnimations) {
         try {
             if (DEBUG) {
-                Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
+                Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync + " waitForAnimations: "
+                        + waitForAnimations);
             }
             // Calling out without a lock held.
-            return mUiAutomationConnection.injectInputEvent(event, sync);
+            return mUiAutomationConnection.injectInputEvent(event, sync, waitForAnimations);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while injecting input event!", re);
         }
@@ -726,7 +751,26 @@
     public void syncInputTransactions() {
         try {
             // Calling out without a lock held.
-            mUiAutomationConnection.syncInputTransactions();
+            mUiAutomationConnection.syncInputTransactions(true /* waitForAnimations */);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while syncing input transactions!", re);
+        }
+    }
+
+    /**
+     * A request for WindowManagerService to wait until all input information has been sent from
+     * WindowManager to native InputManager and optionally wait for animations to complete.
+     *
+     * @param waitForAnimations Whether to wait for all window container animations and surface
+     *   operations to complete.
+     *
+     * @hide
+     */
+    @TestApi
+    public void syncInputTransactions(boolean waitForAnimations) {
+        try {
+            // Calling out without a lock held.
+            mUiAutomationConnection.syncInputTransactions(waitForAnimations);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while syncing input transactions!", re);
         }
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 290e121..7036b6e 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -124,7 +124,7 @@
     }
 
     @Override
-    public boolean injectInputEvent(InputEvent event, boolean sync) {
+    public boolean injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations) {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
             throwIfShutdownLocked();
@@ -134,7 +134,8 @@
                 : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mWindowManager.injectInputAfterTransactionsApplied(event, mode);
+            return mWindowManager.injectInputAfterTransactionsApplied(event, mode,
+                    waitForAnimations);
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -143,7 +144,7 @@
     }
 
     @Override
-    public void syncInputTransactions() {
+    public void syncInputTransactions(boolean waitForAnimations) {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
             throwIfShutdownLocked();
@@ -151,12 +152,11 @@
         }
 
         try {
-            mWindowManager.syncInputTransactions();
+            mWindowManager.syncInputTransactions(waitForAnimations);
         } catch (RemoteException e) {
         }
     }
 
-
     @Override
     public boolean setRotation(int rotation) {
         synchronized (mLock) {
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index d65be9b..77891e0 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -40,14 +40,21 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+            LAUNCH_STATE_UNKNOWN,
             LAUNCH_STATE_COLD,
             LAUNCH_STATE_WARM,
-            LAUNCH_STATE_HOT
+            LAUNCH_STATE_HOT,
+            LAUNCH_STATE_RELAUNCH
     })
     public @interface LaunchState {
     }
 
     /**
+     * Not considered as a launch event, e.g. the activity is already on top.
+     */
+    public static final int LAUNCH_STATE_UNKNOWN = 0;
+
+    /**
      * Cold launch sequence: a new process has started.
      */
     public static final int LAUNCH_STATE_COLD = 1;
@@ -62,6 +69,13 @@
      */
     public static final int LAUNCH_STATE_HOT = 3;
 
+    /**
+     * Relaunch launch sequence: process reused, but activity has to be destroyed and created.
+     * E.g. the current device configuration is different from the background activity that will be
+     * brought to foreground, and the activity doesn't declare to handle the change.
+     */
+    public static final int LAUNCH_STATE_RELAUNCH = 4;
+
     public static final int INVALID_DELAY = -1;
     public int result;
     public boolean timeout;
@@ -124,6 +138,8 @@
                 return "WARM";
             case LAUNCH_STATE_HOT:
                 return "HOT";
+            case LAUNCH_STATE_RELAUNCH:
+                return "RELAUNCH";
             default:
                 return "UNKNOWN (" + type + ")";
         }
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
new file mode 100644
index 0000000..f5674e5
--- /dev/null
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -0,0 +1,273 @@
+/*
+ * 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.people;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.notification.StatusBarNotification;
+
+/**
+ * The People Space tile contains all relevant information to render a tile in People Space: namely
+ * the data of any visible conversation notification associated, associated statuses, and the last
+ * interaction time.
+ *
+ * @hide
+ */
+public class PeopleSpaceTile implements Parcelable {
+
+    private String mId;
+    private CharSequence mUserName;
+    private Icon mUserIcon;
+    private int mUid;
+    private Uri mContactUri;
+    private String mPackageName;
+    private long mLastInteractionTimestamp;
+    private boolean mIsImportantConversation;
+    private boolean mIsHiddenConversation;
+    private StatusBarNotification mNotification;
+    private Intent mIntent;
+    // TODO: add a List of the Status objects once created
+
+    private PeopleSpaceTile(Builder b) {
+        mId = b.mId;
+        mUserName = b.mUserName;
+        mUserIcon = b.mUserIcon;
+        mContactUri = b.mContactUri;
+        mUid = b.mUid;
+        mPackageName = b.mPackageName;
+        mLastInteractionTimestamp = b.mLastInteractionTimestamp;
+        mIsImportantConversation = b.mIsImportantConversation;
+        mIsHiddenConversation = b.mIsHiddenConversation;
+        mNotification = b.mNotification;
+        mIntent = b.mIntent;
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    public CharSequence getUserName() {
+        return mUserName;
+    }
+
+    public Icon getUserIcon() {
+        return mUserIcon;
+    }
+
+    /** Returns the Uri associated with the user in Android Contacts database. */
+    public Uri getContactUri() {
+        return mContactUri;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /** Returns the timestamp of the last interaction. */
+    public long getLastInteractionTimestamp() {
+        return mLastInteractionTimestamp;
+    }
+
+    /**
+     * Whether the conversation is important.
+     */
+    public boolean isImportantConversation() {
+        return mIsImportantConversation;
+    }
+
+    /**
+     * Whether the conversation should be hidden.
+     */
+    public boolean isHiddenConversation() {
+        return mIsHiddenConversation;
+    }
+
+    /**
+     * If a notification is currently active that maps to the relevant shortcut ID, provides the
+     * {@link StatusBarNotification} associated.
+     */
+    public StatusBarNotification getNotification() {
+        return mNotification;
+    }
+
+    /**
+     * Provides an intent to launch. If present, we should manually launch the intent on tile
+     * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID.
+     *
+     * <p>This field should only be used if manually constructing a tile without an associated
+     * shortcut to launch (i.e. birthday tiles).
+     */
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /** Builder to create a {@link PeopleSpaceTile}. */
+    public static class Builder {
+        private String mId;
+        private CharSequence mUserName;
+        private Icon mUserIcon;
+        private Uri mContactUri;
+        private int mUid;
+        private String mPackageName;
+        private long mLastInteractionTimestamp;
+        private boolean mIsImportantConversation;
+        private boolean mIsHiddenConversation;
+        private StatusBarNotification mNotification;
+        private Intent mIntent;
+
+        /** Builder for use only if a shortcut is not available for the tile. */
+        public Builder(String id, String userName, Icon userIcon, Intent intent) {
+            mId = id;
+            mUserName = userName;
+            mUserIcon = userIcon;
+            mIntent = intent;
+            mPackageName = intent == null ? null : intent.getPackage();
+        }
+
+        public Builder(ShortcutInfo info) {
+            mId = info.getId();
+            mUserName = info.getLabel();
+            mUserIcon = info.getIcon();
+            mUid = info.getUserId();
+            mPackageName = info.getPackage();
+        }
+
+        /** Sets the ID for the tile. */
+        public Builder setId(String id) {
+            mId = id;
+            return this;
+        }
+
+        /** Sets the user name. */
+        public Builder setUserName(CharSequence userName) {
+            mUserName = userName;
+            return this;
+        }
+
+        /** Sets the icon shown for the user. */
+        public Builder setUserIcon(Icon userIcon) {
+            mUserIcon = userIcon;
+            return this;
+        }
+
+        /** Sets the Uri associated with the user in Android Contacts database. */
+        public Builder setContactUri(Uri uri) {
+            mContactUri = uri;
+            return this;
+        }
+
+        /** Sets the associated uid. */
+        public Builder setUid(int uid) {
+            mUid = uid;
+            return this;
+        }
+
+        /** Sets the package shown that provided the information. */
+        public Builder setPackageName(String packageName) {
+            mPackageName = packageName;
+            return this;
+        }
+
+        /** Sets the last interaction timestamp. */
+        public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) {
+            mLastInteractionTimestamp = lastInteractionTimestamp;
+            return this;
+        }
+
+        /** Sets whether the conversation is important. */
+        public Builder setIsImportantConversation(boolean isImportantConversation) {
+            mIsImportantConversation = isImportantConversation;
+            return this;
+        }
+
+        /** Sets whether the conversation is hidden. */
+        public Builder setIsHiddenConversation(boolean isHiddenConversation) {
+            mIsHiddenConversation = isHiddenConversation;
+            return this;
+        }
+
+        /** Sets the associated notification. */
+        public Builder setNotification(StatusBarNotification notification) {
+            mNotification = notification;
+            return this;
+        }
+
+        /** Sets an intent to launch on click. */
+        public Builder setIntent(Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /** Builds a {@link PeopleSpaceTile}. */
+        @NonNull
+        public PeopleSpaceTile build() {
+            return new PeopleSpaceTile(this);
+        }
+    }
+
+    private PeopleSpaceTile(Parcel in) {
+        mId = in.readString();
+        mUserName = in.readCharSequence();
+        mUserIcon = in.readParcelable(Icon.class.getClassLoader());
+        mUid = in.readInt();
+        mPackageName = in.readString();
+        mLastInteractionTimestamp = in.readLong();
+        mIsImportantConversation = in.readBoolean();
+        mIsHiddenConversation = in.readBoolean();
+        mNotification = in.readParcelable(StatusBarNotification.class.getClassLoader());
+        mIntent = in.readParcelable(Intent.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeCharSequence(mUserName);
+        dest.writeParcelable(mUserIcon, flags);
+        dest.writeInt(mUid);
+        dest.writeString(mPackageName);
+        dest.writeLong(mLastInteractionTimestamp);
+        dest.writeParcelable(mNotification, flags);
+        dest.writeBoolean(mIsImportantConversation);
+        dest.writeBoolean(mIsHiddenConversation);
+        dest.writeParcelable(mIntent, flags);
+    }
+
+    public static final @android.annotation.NonNull
+            Creator<PeopleSpaceTile> CREATOR = new Creator<PeopleSpaceTile>() {
+                public PeopleSpaceTile createFromParcel(Parcel source) {
+                    return new PeopleSpaceTile(source);
+                }
+
+                public PeopleSpaceTile[] newArray(int size) {
+                    return new PeopleSpaceTile[size];
+                }
+            };
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 37e0a44..6a3f6b4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -63,6 +63,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
+import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -6285,4 +6286,25 @@
     public boolean isUiContext() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
+
+    /**
+     * Returns {@code true} if the context is a UI context which can access UI components such as
+     * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
+     * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
+     * contexts throws {@link android.os.strictmode.Violation} if
+     * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
+     * <p>
+     * Examples of UI contexts are
+     * an {@link android.app.Activity Activity}, a context created from
+     * {@link #createWindowContext(int, Bundle)} or
+     * {@link android.inputmethodservice.InputMethodService InputMethodService}
+     * </p>
+     *
+     * @see #getDisplay()
+     * @see #getSystemService(String)
+     * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+     */
+    public static boolean isUiContext(@NonNull Context context) {
+        return context.isUiContext();
+    }
 }
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index fd3d48f..217f637c 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -23,7 +23,7 @@
 import android.annotation.SystemService;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.content.Context;
 import android.os.Build;
 import android.os.Process;
@@ -90,7 +90,7 @@
      * java.lang.IllegalStateException}, which existed in the source prior to R.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R)
     private static final long THROW_SECURITY_EXCEPTIONS = 147340954;
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d66a42a..f634b8a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -38,6 +38,7 @@
 import android.content.pm.KeySet;
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.PermissionGroupInfo;
@@ -797,4 +798,7 @@
     IBinder getHoldLockToken();
 
     void holdLock(in IBinder token, in int durationMs);
+
+    PackageManager.Property getProperty(String propertyName, String packageName, String className);
+    ParceledListSlice queryProperty(String propertyName, int componentType);
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index c964b4b..4b7bbbf 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -433,6 +433,16 @@
          */
         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
 
+        /**
+         * Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
+         *
+         * <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final int FLAG_GET_PERSONS_DATA = 1 << 11;
+
         /** @hide */
         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
                 FLAG_MATCH_DYNAMIC,
@@ -440,6 +450,7 @@
                 FLAG_MATCH_MANIFEST,
                 FLAG_MATCH_CACHED,
                 FLAG_GET_KEY_FIELDS_ONLY,
+                FLAG_GET_PERSONS_DATA,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface QueryFlags {}
diff --git a/core/java/android/content/pm/PackageManager.aidl b/core/java/android/content/pm/PackageManager.aidl
new file mode 100644
index 0000000..31365a1
--- /dev/null
+++ b/core/java/android/content/pm/PackageManager.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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 PackageManager.Property;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cc484de..044b3b2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -64,6 +64,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -75,6 +77,7 @@
 import android.util.AndroidException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
 import dalvik.system.VMRuntime;
@@ -116,6 +119,243 @@
     }
 
     /**
+     * A property value set within the manifest.
+     * <p>
+     * The value of a property will only have a single type, as defined by
+     * the property itself.
+     */
+    public static final class Property implements Parcelable {
+        private static final int TYPE_BOOLEAN = 1;
+        private static final int TYPE_FLOAT = 2;
+        private static final int TYPE_INTEGER = 3;
+        private static final int TYPE_RESOURCE = 4;
+        private static final int TYPE_STRING = 5;
+        private final String mName;
+        private final int mType;
+        private final String mClassName;
+        private final String mPackageName;
+        private boolean mBooleanValue;
+        private float mFloatValue;
+        private int mIntegerValue;
+        private String mStringValue;
+
+        /** @hide */
+        @VisibleForTesting
+        public Property(@NonNull String name, int type,
+                @NonNull String packageName, @Nullable String className) {
+            assert name != null;
+            assert type >= TYPE_BOOLEAN && type <= TYPE_STRING;
+            assert packageName != null;
+            this.mName = name;
+            this.mType = type;
+            this.mPackageName = packageName;
+            this.mClassName = className;
+        }
+        /** @hide */
+        public Property(@NonNull String name, boolean value,
+                String packageName, String className) {
+            this(name, TYPE_BOOLEAN, packageName, className);
+            mBooleanValue = value;
+        }
+        /** @hide */
+        public Property(@NonNull String name, float value,
+                String packageName, String className) {
+            this(name, TYPE_FLOAT, packageName, className);
+            mFloatValue = value;
+        }
+        /** @hide */
+        public Property(@NonNull String name, int value, boolean isResource,
+                String packageName, String className) {
+            this(name, isResource ? TYPE_RESOURCE : TYPE_INTEGER, packageName, className);
+            mIntegerValue = value;
+        }
+        /** @hide */
+        public Property(@NonNull String name, String value,
+                String packageName, String className) {
+            this(name, TYPE_STRING, packageName, className);
+            mStringValue = value;
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Returns the name of the property.
+         */
+        @NonNull public String getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the name of the package where this this property was defined.
+         */
+        @NonNull public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * Returns the classname of the component where this property was defined.
+         * <p>If the property was defined within and &lt;application&gt; tag, retutrns
+         * {@code null}
+         */
+        @Nullable public String getClassName() {
+            return mClassName;
+        }
+
+        /**
+         * Returns the boolean value set for the property.
+         * <p>If the property is not of a boolean type, returns {@code false}.
+         */
+        public boolean getBoolean() {
+            return mBooleanValue;
+        }
+
+        /**
+         * Returns {@code true} if the property is a boolean type. Otherwise {@code false}.
+         */
+        public boolean isBoolean() {
+            return mType == TYPE_BOOLEAN;
+        }
+
+        /**
+         * Returns the float value set for the property.
+         * <p>If the property is not of a float type, returns {@code 0.0}.
+         */
+        public float getFloat() {
+            return mFloatValue;
+        }
+
+        /**
+         * Returns {@code true} if the property is a float type. Otherwise {@code false}.
+         */
+        public boolean isFloat() {
+            return mType == TYPE_FLOAT;
+        }
+
+        /**
+         * Returns the integer value set for the property.
+         * <p>If the property is not of an integer type, returns {@code 0}.
+         */
+        public int getInteger() {
+            return mType == TYPE_INTEGER ? mIntegerValue : 0;
+        }
+
+        /**
+         * Returns {@code true} if the property is an integer type. Otherwise {@code false}.
+         */
+        public boolean isInteger() {
+            return mType == TYPE_INTEGER;
+        }
+
+        /**
+         * Returns the a resource id set for the property.
+         * <p>If the property is not of a resource id type, returns {@code 0}.
+         */
+        public int getResourceId() {
+            return mType == TYPE_RESOURCE ? mIntegerValue : 0;
+        }
+
+        /**
+         * Returns {@code true} if the property is a resource id type. Otherwise {@code false}.
+         */
+        public boolean isResourceId() {
+            return mType == TYPE_RESOURCE;
+        }
+
+        /**
+         * Returns the a String value set for the property.
+         * <p>If the property is not a String type, returns {@code null}.
+         */
+        @Nullable public String getString() {
+            return mStringValue;
+        }
+
+        /**
+         * Returns {@code true} if the property is a String type. Otherwise {@code false}.
+         */
+        public boolean isString() {
+            return mType == TYPE_STRING;
+        }
+
+        /**
+         * Adds a mapping from the given key to this property's value in the provided
+         * {@link android.os.Bundle}. If the provided {@link android.os.Bundle} is
+         * {@code null}, creates a new {@link android.os.Bundle}.
+         * @hide
+         */
+        public Bundle toBundle(Bundle outBundle) {
+            final Bundle b = outBundle == null ? new Bundle() : outBundle;
+            if (mType == TYPE_BOOLEAN) {
+                b.putBoolean(mName, mBooleanValue);
+            } else if (mType == TYPE_FLOAT) {
+                b.putFloat(mName, mFloatValue);
+            } else if (mType == TYPE_INTEGER) {
+                b.putInt(mName, mIntegerValue);
+            } else if (mType == TYPE_RESOURCE) {
+                b.putInt(mName, mIntegerValue);
+            } else if (mType == TYPE_STRING) {
+                b.putString(mName, mStringValue);
+            }
+            return b;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString(mName);
+            dest.writeInt(mType);
+            dest.writeString(mPackageName);
+            dest.writeString(mClassName);
+            if (mType == TYPE_BOOLEAN) {
+                dest.writeBoolean(mBooleanValue);
+            } else if (mType == TYPE_FLOAT) {
+                dest.writeFloat(mFloatValue);
+            } else if (mType == TYPE_INTEGER) {
+                dest.writeInt(mIntegerValue);
+            } else if (mType == TYPE_RESOURCE) {
+                dest.writeInt(mIntegerValue);
+            } else if (mType == TYPE_STRING) {
+                dest.writeString(mStringValue);
+            }
+        }
+
+        @NonNull
+        public static final Creator<Property> CREATOR = new Creator<Property>() {
+            @Override
+            public Property createFromParcel(@NonNull Parcel source) {
+                final String name = source.readString();
+                final int type = source.readInt();
+                final String packageName = source.readString();
+                final String className = source.readString();
+                if (type == TYPE_BOOLEAN) {
+                    return new Property(name, source.readBoolean(), packageName, className);
+                } else if (type == TYPE_FLOAT) {
+                    return new Property(name, source.readFloat(), packageName, className);
+                } else if (type == TYPE_INTEGER) {
+                    return new Property(name, source.readInt(), false, packageName, className);
+                } else if (type == TYPE_RESOURCE) {
+                    return new Property(name, source.readInt(), true, packageName, className);
+                } else if (type == TYPE_STRING) {
+                    return new Property(name, source.readString(), packageName, className);
+                }
+                return null;
+            }
+
+            @Override
+            public Property[] newArray(int size) {
+                return new Property[size];
+            }
+        };
+    }
+
+    /**
      * Listener for changes in permissions granted to a UID.
      *
      * @hide
@@ -130,6 +370,41 @@
         public void onPermissionsChanged(int uid);
     }
 
+    /** @hide */
+    public static final int TYPE_UNKNOWN = 0;
+    /** @hide */
+    public static final int TYPE_ACTIVITY = 1;
+    /** @hide */
+    public static final int TYPE_RECEIVER = 2;
+    /** @hide */
+    public static final int TYPE_SERVICE = 3;
+    /** @hide */
+    public static final int TYPE_PROVIDER = 4;
+    /** @hide */
+    public static final int TYPE_APPLICATION = 5;
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN,
+            TYPE_ACTIVITY,
+            TYPE_RECEIVER,
+            TYPE_SERVICE,
+            TYPE_PROVIDER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComponentType {}
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN,
+            TYPE_ACTIVITY,
+            TYPE_RECEIVER,
+            TYPE_SERVICE,
+            TYPE_PROVIDER,
+            TYPE_APPLICATION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PropertyLocation {}
+
     /**
      * As a guiding principle:
      * <p>
@@ -8124,7 +8399,7 @@
      * Trust any Installer to provide checksums for the package.
      * @see #requestChecksums
      */
-    public static final @Nullable List<Certificate> TRUST_ALL = Collections.singletonList(null);
+    public static final @NonNull List<Certificate> TRUST_ALL = Collections.singletonList(null);
 
     /**
      * Don't trust any Installer to provide checksums for the package.
@@ -8363,6 +8638,87 @@
     }
 
     /**
+     * Returns the property defined in the given package's &lt;appliction&gt; tag.
+     *
+     * @throws NameNotFoundException if either the given package is not installed or if the
+     * given property is not defined within the &lt;application&gt; tag.
+     */
+    @NonNull
+    public Property getProperty(@NonNull String propertyName, @NonNull String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property defined in the given component declaration.
+     *
+     * @throws NameNotFoundException if either the given component does not exist or if the
+     * given property is not defined within the component declaration.
+     */
+    @NonNull
+    public Property getProperty(@NonNull String propertyName, @NonNull ComponentName component)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property definition for all &lt;application&gt; tags.
+     * <p>If the property is not defined with any &lt;application&gt; tag,
+     * returns and empty list.
+     */
+    @NonNull
+    public List<Property> queryApplicationProperty(@NonNull String propertyName) {
+        throw new UnsupportedOperationException(
+                "qeuryApplicationProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property definition for all &lt;activity&gt; and &lt;activity-alias&gt; tags.
+     * <p>If the property is not defined with any &lt;activity&gt; and &lt;activity-alias&gt; tag,
+     * returns and empty list.
+     */
+    @NonNull
+    public List<Property> queryActivityProperty(@NonNull String propertyName) {
+        throw new UnsupportedOperationException(
+                "qeuryActivityProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property definition for all &lt;provider&gt; tags.
+     * <p>If the property is not defined with any &lt;provider&gt; tag,
+     * returns and empty list.
+     */
+    @NonNull
+    public List<Property> queryProviderProperty(@NonNull String propertyName) {
+        throw new UnsupportedOperationException(
+                "qeuryProviderProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property definition for all &lt;receiver&gt; tags.
+     * <p>If the property is not defined with any &lt;receiver&gt; tag,
+     * returns and empty list.
+     */
+    @NonNull
+    public List<Property> queryReceiverProperty(@NonNull String propertyName) {
+        throw new UnsupportedOperationException(
+                "qeuryReceiverProperty not implemented in subclass");
+    }
+
+    /**
+     * Returns the property definition for all &lt;service&gt; tags.
+     * <p>If the property is not defined with any &lt;service&gt; tag,
+     * returns and empty list.
+     */
+    @NonNull
+    public List<Property> queryServiceProperty(@NonNull String propertyName) {
+        throw new UnsupportedOperationException(
+                "qeuryServiceProperty not implemented in subclass");
+    }
+
+    /**
      * Grants implicit visibility of the package that provides an authority to a querying UID.
      *
      * @throws SecurityException when called by a package other than the contacts provider
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 9cda4d3..8147599 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -23,6 +23,7 @@
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
@@ -75,6 +76,9 @@
 
     ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
 
+    /** Add a property to the application scope */
+    ParsingPackage addProperty(Property property);
+
     ParsingPackage addProtectedBroadcast(String protectedBroadcast);
 
     ParsingPackage addProvider(ParsedProvider parsedProvider);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 2a15e02..b826b7a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
@@ -273,6 +274,9 @@
     @Nullable
     private Bundle metaData;
 
+    @NonNull
+    private Map<String, Property> mProperties = emptyMap();
+
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     protected String volumeUuid;
@@ -623,6 +627,15 @@
     }
 
     @Override
+    public ParsingPackageImpl addProperty(@Nullable Property property) {
+        if (property == null) {
+            return this;
+        }
+        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
         if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
             this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
@@ -1172,6 +1185,7 @@
         dest.writeInt(this.gwpAsanMode);
         dest.writeSparseIntArray(this.minExtensionVersions);
         dest.writeLong(this.mBooleans);
+        dest.writeMap(this.mProperties);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1290,7 +1304,7 @@
         this.gwpAsanMode = in.readInt();
         this.minExtensionVersions = in.readSparseIntArray();
         this.mBooleans = in.readLong();
-
+        this.mProperties = in.createTypedArrayMap(Property.CREATOR);
         assignDerivedFields();
     }
 
@@ -1528,6 +1542,12 @@
 
     @NonNull
     @Override
+    public Map<String, Property> getProperties() {
+        return mProperties;
+    }
+
+    @NonNull
+    @Override
     public Set<String> getUpgradeKeySets() {
         return upgradeKeySets;
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index acd6305..13ae7a2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -25,6 +25,7 @@
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.ServiceInfo;
 import android.content.pm.parsing.component.ParsedActivity;
@@ -43,8 +44,6 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
-import com.android.internal.R;
-
 import java.security.PublicKey;
 import java.util.List;
 import java.util.Map;
@@ -204,6 +203,12 @@
     List<String> getRequestedPermissions();
 
     /**
+     * Returns the properties set on the application
+     */
+    @NonNull
+    Map<String, Property> getProperties();
+
+    /**
      * Whether or not the app requested explicitly resizeable Activities.
      * A null value means nothing was explicitly requested.
      */
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 6196854..eae7d45 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -42,6 +42,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.SigningDetails;
@@ -51,6 +52,7 @@
 import android.content.pm.parsing.component.ParsedActivityUtils;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedAttributionUtils;
+import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedInstrumentationUtils;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -699,12 +701,19 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
-                        pkg.getMetaData(), input);
-                if (metaDataResult.isSuccess()) {
-                    pkg.setMetaData(metaDataResult.getResult());
+                ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res,
+                        parser, "<meta-data>", input);
+                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
                 }
                 return metaDataResult;
+            case "property":
+                ParseResult<Property> propertyResult = parseMetaData(pkg, null, res,
+                        parser, "<property>", input);
+                if (propertyResult.isSuccess()) {
+                    pkg.addProperty(propertyResult.getResult());
+                }
+                return propertyResult;
             case "uses-static-library":
                 return parseUsesStaticLibrary(input, pkg, res, parser);
             case "uses-library":
@@ -2085,13 +2094,19 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
-                        pkg.getMetaData(), input);
-                if (metaDataResult.isSuccess()) {
-                    pkg.setMetaData(metaDataResult.getResult());
+                final ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res,
+                        parser, "<meta-data>", input);
+                if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+                    pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
                 }
-
                 return metaDataResult;
+            case "property":
+                final ParseResult<Property> propertyResult = parseMetaData(pkg, null, res,
+                        parser, "<property>", input);
+                if (propertyResult.isSuccess()) {
+                    pkg.addProperty(propertyResult.getResult());
+                }
+                return propertyResult;
             case "static-library":
                 return parseStaticLibrary(pkg, res, parser, input);
             case "library":
@@ -2736,57 +2751,60 @@
                 : input.error("must have at least one '.' separator");
     }
 
-    public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, Bundle data, ParseInput input) {
+    /**
+     * Parse a meta data defined on the enclosing tag.
+     * <p>Meta data can be defined by either &lt;meta-data&gt; or &lt;property&gt; elements.
+     */
+    public static ParseResult<Property> parseMetaData(ParsingPackage pkg, ParsedComponent component,
+            Resources res, XmlResourceParser parser, String tagName, ParseInput input) {
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
         try {
-            if (data == null) {
-                data = new Bundle();
-            }
-
-            String name = TextUtils.safeIntern(
+            final Property property;
+            final String name = TextUtils.safeIntern(
                     nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
             if (name == null) {
-                return input.error("<meta-data> requires an android:name attribute");
+                return input.error(tagName + " requires an android:name attribute");
             }
 
+            final String packageName = pkg.getPackageName();
+            final String className = component != null ? component.getName() : null;
             TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
             if (v != null && v.resourceId != 0) {
-                //Slog.i(TAG, "Meta data ref " + name + ": " + v);
-                data.putInt(name, v.resourceId);
+                property = new Property(name, v.resourceId, true, packageName, className);
             } else {
                 v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
-                //Slog.i(TAG, "Meta data " + name + ": " + v);
                 if (v != null) {
                     if (v.type == TypedValue.TYPE_STRING) {
-                        CharSequence cs = v.coerceToString();
-                        data.putString(name, cs != null ? cs.toString() : null);
+                        final CharSequence cs = v.coerceToString();
+                        final String stringValue = cs != null ? cs.toString() : null;
+                        property = new Property(name, stringValue, packageName, className);
                     } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
-                        data.putBoolean(name, v.data != 0);
+                        property = new Property(name, v.data != 0, packageName, className);
                     } else if (v.type >= TypedValue.TYPE_FIRST_INT
                             && v.type <= TypedValue.TYPE_LAST_INT) {
-                        data.putInt(name, v.data);
+                        property = new Property(name, v.data, false, packageName, className);
                     } else if (v.type == TypedValue.TYPE_FLOAT) {
-                        data.putFloat(name, v.getFloat());
+                        property = new Property(name, v.getFloat(), packageName, className);
                     } else {
                         if (!PackageParser.RIGID_PARSER) {
                             Slog.w(TAG,
-                                    "<meta-data> only supports string, integer, float, color, "
+                                    tagName + " only supports string, integer, float, color, "
                                             + "boolean, and resource reference types: "
                                             + parser.getName() + " at "
                                             + pkg.getBaseApkPath() + " "
                                             + parser.getPositionDescription());
+                            property = null;
                         } else {
-                            return input.error("<meta-data> only supports string, integer, float, "
+                            return input.error(tagName + " only supports string, integer, float, "
                                     + "color, boolean, and resource reference types");
                         }
                     }
                 } else {
-                    return input.error("<meta-data> requires an android:value "
+                    return input.error(tagName + " requires an android:value "
                             + "or android:resource attribute");
                 }
             }
-            return input.success(data);
+            return input.success(property);
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 511ee5d..0d7198d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -356,6 +356,8 @@
                 result = intentResult;
             } else if (parser.getName().equals("meta-data")) {
                 result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
+            } else if (parser.getName().equals("property")) {
+                result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
             } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
                 ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                         true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 6323d69..4aed77a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -18,10 +18,13 @@
 
 import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
 
+import static java.util.Collections.emptyMap;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.pm.PackageManager.Property;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /** @hide */
 public abstract class ParsedComponent implements Parcelable {
@@ -70,6 +74,8 @@
     @Nullable
     protected Bundle metaData;
 
+    private Map<String, Property> mProperties = emptyMap();
+
     ParsedComponent() {
 
     }
@@ -96,6 +102,11 @@
         this.intents = CollectionUtils.add(this.intents, intent);
     }
 
+    /** Add a property to the component */
+    public void addProperty(@NonNull Property property) {
+        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+    }
+
     @NonNull
     public List<ParsedIntentInfo> getIntents() {
         return intents != null ? intents : Collections.emptyList();
@@ -142,6 +153,7 @@
         sForInternedString.parcel(this.packageName, dest, flags);
         sForIntentInfos.parcel(this.getIntents(), dest, flags);
         dest.writeBundle(this.metaData);
+        dest.writeMap(this.mProperties);
     }
 
     protected ParsedComponent(Parcel in) {
@@ -160,6 +172,7 @@
         this.packageName = sForInternedString.unparcel(in);
         this.intents = sForIntentInfos.unparcel(in);
         this.metaData = in.readBundle(boot);
+        this.mProperties = in.createTypedArrayMap(Property.CREATOR);
     }
 
     @NonNull
@@ -205,4 +218,9 @@
     public Bundle getMetaData() {
         return metaData;
     }
+
+    @NonNull
+    public Map<String, Property> getProperties() {
+        return mProperties;
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index dd2fb5b..46b9419 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
@@ -97,13 +98,29 @@
 
     static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg,
             Resources resources, XmlResourceParser parser, ParseInput input) {
-        ParseResult<Bundle> result = ParsingPackageUtils.parseMetaData(pkg, resources,
-                parser, component.metaData, input);
+        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
+                resources, parser, "<meta-data>", input);
         if (result.isError()) {
             return input.error(result);
         }
-        Bundle bundle = result.getResult();
-        component.metaData = bundle;
-        return input.success(bundle);
+        final Property property = result.getResult();
+        if (property != null) {
+            component.metaData = property.toBundle(component.metaData);
+        }
+        return input.success(component.metaData);
+    }
+
+    static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg,
+            Resources resources, XmlResourceParser parser, ParseInput input) {
+        ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
+                resources, parser, "<property>", input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        final Property property = result.getResult();
+        if (property != null) {
+            component.addProperty(property);
+        }
+        return input.success(property);
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 37cbeca..90691f1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -180,6 +180,9 @@
                 case "meta-data":
                     result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
                     break;
+                case "property":
+                    result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
+                    break;
                 case "grant-uri-permission": {
                     result = parseGrantUriPermission(provider, pkg, res, parser, input);
                     break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index afe3c54..739bee2 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -145,6 +145,10 @@
                 case "meta-data":
                     parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
                     break;
+                case "property":
+                    parseResult =
+                            ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
+                    break;
                 default:
                     parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
                     break;
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
new file mode 100644
index 0000000..f34e2bf
--- /dev/null
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -0,0 +1,202 @@
+/*
+ * 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.hardware;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+/**
+ * The camera action state used for passing camera usage information from
+ * camera service to camera service proxy .
+ *
+ * Include camera id, facing, state, client apk name, apiLevel, isNdk,
+ * and session/stream statistics.
+ *
+ * @hide
+ */
+public class CameraSessionStats implements Parcelable {
+    public static final int CAMERA_STATE_OPEN = 0;
+    public static final int CAMERA_STATE_ACTIVE = 1;
+    public static final int CAMERA_STATE_IDLE = 2;
+    public static final int CAMERA_STATE_CLOSED = 3;
+
+    /**
+     * Values for notifyCameraState facing
+     */
+    public static final int CAMERA_FACING_BACK = 0;
+    public static final int CAMERA_FACING_FRONT = 1;
+    public static final int CAMERA_FACING_EXTERNAL = 2;
+
+    /**
+     * Values for notifyCameraState api level
+     */
+    public static final int CAMERA_API_LEVEL_1 = 1;
+    public static final int CAMERA_API_LEVEL_2 = 2;
+
+    private String mCameraId;
+    private int mFacing;
+    private int mNewCameraState;
+    private String mClientName;
+    private int mApiLevel;
+    private boolean mIsNdk;
+    private int mLatencyMs;
+    private int mSessionType;
+    private int mInternalReconfigure;
+    private long mRequestCount;
+    private long mResultErrorCount;
+    private boolean mDeviceError;
+    private ArrayList<CameraStreamStats> mStreamStats;
+
+    public CameraSessionStats() {
+        mFacing = -1;
+        mNewCameraState = -1;
+        mApiLevel = -1;
+        mIsNdk = false;
+        mLatencyMs = -1;
+        mSessionType = -1;
+        mInternalReconfigure = -1;
+        mRequestCount = 0;
+        mResultErrorCount = 0;
+        mDeviceError = false;
+        mStreamStats = new ArrayList<CameraStreamStats>();
+    }
+
+    public CameraSessionStats(String cameraId, int facing, int newCameraState,
+            String clientName, int apiLevel, boolean isNdk, int creationDuration,
+            int sessionType, int internalReconfigure) {
+        mCameraId = cameraId;
+        mFacing = facing;
+        mNewCameraState = newCameraState;
+        mClientName = clientName;
+        mApiLevel = apiLevel;
+        mIsNdk = isNdk;
+        mLatencyMs = creationDuration;
+        mSessionType = sessionType;
+        mInternalReconfigure = internalReconfigure;
+        mStreamStats = new ArrayList<CameraStreamStats>();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CameraSessionStats> CREATOR =
+            new Parcelable.Creator<CameraSessionStats>() {
+        @Override
+        public CameraSessionStats createFromParcel(Parcel in) {
+            return new CameraSessionStats(in);
+        }
+
+        @Override
+        public CameraSessionStats[] newArray(int size) {
+            return new CameraSessionStats[size];
+        }
+    };
+
+    private CameraSessionStats(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mCameraId);
+        dest.writeInt(mFacing);
+        dest.writeInt(mNewCameraState);
+        dest.writeString(mClientName);
+        dest.writeInt(mApiLevel);
+        dest.writeBoolean(mIsNdk);
+        dest.writeInt(mLatencyMs);
+        dest.writeInt(mSessionType);
+        dest.writeInt(mInternalReconfigure);
+        dest.writeLong(mRequestCount);
+        dest.writeLong(mResultErrorCount);
+        dest.writeBoolean(mDeviceError);
+        dest.writeTypedList(mStreamStats);
+    }
+
+    public void readFromParcel(Parcel in) {
+        mCameraId = in.readString();
+        mFacing = in.readInt();
+        mNewCameraState = in.readInt();
+        mClientName = in.readString();
+        mApiLevel = in.readInt();
+        mIsNdk = in.readBoolean();
+        mLatencyMs = in.readInt();
+        mSessionType = in.readInt();
+        mInternalReconfigure = in.readInt();
+        mRequestCount = in.readLong();
+        mResultErrorCount = in.readLong();
+        mDeviceError = in.readBoolean();
+
+        ArrayList<CameraStreamStats> streamStats = new ArrayList<CameraStreamStats>();
+        in.readTypedList(streamStats, CameraStreamStats.CREATOR);
+        mStreamStats = streamStats;
+    }
+
+    public String getCameraId() {
+        return mCameraId;
+    }
+
+    public int getFacing() {
+        return mFacing;
+    }
+
+    public int getNewCameraState() {
+        return mNewCameraState;
+    }
+
+    public String getClientName() {
+        return mClientName;
+    }
+
+    public int getApiLevel() {
+        return mApiLevel;
+    }
+
+    public boolean isNdk() {
+        return mIsNdk;
+    }
+
+    public int getLatencyMs() {
+        return mLatencyMs;
+    }
+
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    public int getInternalReconfigureCount() {
+        return mInternalReconfigure;
+    }
+
+    public long getRequestCount() {
+        return mRequestCount;
+    }
+
+    public long getResultErrorCount() {
+        return mResultErrorCount;
+    }
+
+    public boolean getDeviceErrorFlag() {
+        return mDeviceError;
+    }
+
+    public List<CameraStreamStats> getStreamStats() {
+        return mStreamStats;
+    }
+}
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
new file mode 100644
index 0000000..ae801b6
--- /dev/null
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -0,0 +1,169 @@
+/*
+ * 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.hardware;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * The camera stream statistics used for passing camera stream information from
+ * camera service to camera service proxy.
+ *
+ * Include camera stream configuration, request/error counts, startup latency,
+ * and latency/jitter histograms.
+ *
+ * @hide
+ */
+public class CameraStreamStats implements Parcelable {
+
+    private int mWidth;
+    private int mHeight;
+    private int mFormat;
+    private int mDataSpace;
+    private long mUsage;
+    private long mRequestCount;
+    private long mErrorCount;
+    private int mStartLatencyMs;
+    private int mMaxHalBuffers;
+    private int mMaxAppBuffers;
+
+    private static final String TAG = "CameraStreamStats";
+
+    public CameraStreamStats() {
+        mWidth = 0;
+        mHeight = 0;
+        mFormat = 0;
+        mDataSpace = 0;
+        mUsage = 0;
+        mRequestCount = 0;
+        mErrorCount = 0;
+        mStartLatencyMs = 0;
+        mMaxHalBuffers = 0;
+        mMaxAppBuffers = 0;
+    }
+
+    public CameraStreamStats(int width, int height, int format,
+            int dataSpace, long usage, long requestCount, long errorCount,
+            int startLatencyMs, int maxHalBuffers, int maxAppBuffers) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = format;
+        mDataSpace = dataSpace;
+        mUsage = usage;
+        mRequestCount = requestCount;
+        mErrorCount = errorCount;
+        mStartLatencyMs = startLatencyMs;
+        mMaxHalBuffers = maxHalBuffers;
+        mMaxAppBuffers = maxAppBuffers;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
+            new Parcelable.Creator<CameraStreamStats>() {
+        @Override
+        public CameraStreamStats createFromParcel(Parcel in) {
+            try {
+                CameraStreamStats streamStats = new CameraStreamStats(in);
+                return streamStats;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating CameraStreamStats from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public CameraStreamStats[] newArray(int size) {
+            return new CameraStreamStats[size];
+        }
+    };
+
+    private CameraStreamStats(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
+        dest.writeInt(mFormat);
+        dest.writeInt(mDataSpace);
+        dest.writeLong(mUsage);
+        dest.writeLong(mRequestCount);
+        dest.writeLong(mErrorCount);
+        dest.writeInt(mStartLatencyMs);
+        dest.writeInt(mMaxHalBuffers);
+        dest.writeInt(mMaxAppBuffers);
+    }
+
+    public void readFromParcel(Parcel in) {
+        mWidth = in.readInt();
+        mHeight = in.readInt();
+        mFormat = in.readInt();
+        mDataSpace = in.readInt();
+        mUsage = in.readLong();
+        mRequestCount = in.readLong();
+        mErrorCount = in.readLong();
+        mStartLatencyMs = in.readInt();
+        mMaxHalBuffers = in.readInt();
+        mMaxAppBuffers = in.readInt();
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public int getFormat() {
+        return mFormat;
+    }
+
+    public int getDataSpace() {
+        return mDataSpace;
+    }
+
+    public long getUsage() {
+        return mUsage;
+    }
+
+    public long getRequestCount() {
+        return mRequestCount;
+    }
+
+    public long getErrorCount() {
+        return mErrorCount;
+    }
+
+    public int getStartLatencyMs() {
+        return mStartLatencyMs;
+    }
+
+    public int getMaxHalBuffers() {
+        return mMaxHalBuffers;
+    }
+
+    public int getMaxAppBuffers() {
+        return mMaxAppBuffers;
+    }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b6f4bd3..9a9163c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -27,6 +27,7 @@
 import android.hardware.camera2.utils.TaskSingleDrainer;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.Surface;
 
@@ -1002,7 +1003,7 @@
                     // begin transition to unconfigured
                     mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
                             /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE,
-                            /*sessionParams*/ null);
+                            /*sessionParams*/ null, SystemClock.uptimeMillis());
                 } catch (CameraAccessException e) {
                     // OK: do not throw checked exceptions.
                     Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 819d966..f564ad7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -45,6 +45,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
@@ -374,7 +375,8 @@
             outputConfigs.add(new OutputConfiguration(s));
         }
         configureStreamsChecked(/*inputConfig*/null, outputConfigs,
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null,
+                SystemClock.uptimeMillis());
 
     }
 
@@ -395,12 +397,15 @@
      * @param operatingMode If the stream configuration is for a normal session,
      *     a constrained high speed session, or something else.
      * @param sessionParams Session parameters.
+     * @param createSessionStartTimeMs The timestamp when session creation starts, measured by
+     *     uptimeMillis().
      * @return whether or not the configuration was successful
      *
      * @throws CameraAccessException if there were any unexpected problems during configuration
      */
     public boolean configureStreamsChecked(InputConfiguration inputConfig,
-            List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
+            List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams,
+            long createSessionStartTime)
                     throws CameraAccessException {
         // Treat a null input the same an empty list
         if (outputs == null) {
@@ -479,9 +484,10 @@
                 int offlineStreamIds[];
                 if (sessionParams != null) {
                     offlineStreamIds = mRemoteDevice.endConfigure(operatingMode,
-                            sessionParams.getNativeCopy());
+                            sessionParams.getNativeCopy(), createSessionStartTime);
                 } else {
-                    offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null);
+                    offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null,
+                            createSessionStartTime);
                 }
 
                 mOfflineSupport.clear();
@@ -650,6 +656,7 @@
             List<OutputConfiguration> outputConfigurations,
             CameraCaptureSession.StateCallback callback, Executor executor,
             int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
+        long createSessionStartTime = SystemClock.uptimeMillis();
         synchronized(mInterfaceLock) {
             if (DEBUG) {
                 Log.d(TAG, "createCaptureSessionInternal");
@@ -677,7 +684,7 @@
             try {
                 // configure streams and then block until IDLE
                 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
-                        operatingMode, sessionParams);
+                        operatingMode, sessionParams, createSessionStartTime);
                 if (configureSuccess == true && inputConfig != null) {
                     input = mRemoteDevice.getInputSurface();
                 }
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index fa7301b..ba4395f 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -110,11 +110,11 @@
         }
     }
 
-    public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams)
-           throws CameraAccessException {
+    public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams,
+            long startTimeMs) throws CameraAccessException {
         try {
             return mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ?
-                    new CameraMetadataNative() : sessionParams);
+                    new CameraMetadataNative() : sessionParams, startTimeMs);
         } catch (Throwable t) {
             CameraManager.throwAsPublicException(t);
             throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index defcab7..5a03ade 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -65,6 +65,12 @@
     public abstract boolean isProximitySensorAvailable();
 
     /**
+     * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided
+     * display belongs.
+     */
+    public abstract int getDisplayGroupId(int displayId);
+
+    /**
      * Screenshot for internal system-only use such as rotation, etc.  This method includes
      * secure layers and the result should never be exposed to non-system applications.
      * This method does not apply any rotation and provides the output in natural orientation.
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index f1534d9..eef4089 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -832,6 +832,22 @@
     }
 
     /**
+     * For CEC source devices (OTT/STB/Audio system): toggle the power status of the HDMI-connected
+     * display and follow the display's new power status.
+     * For all other devices: no functionality.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void toggleAndFollowTvPower() {
+        try {
+            mService.toggleAndFollowTvPower();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Controls whether volume control commands via HDMI CEC are enabled.
      *
      * <p>When disabled:
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index fab56b8..202e090 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -67,6 +67,11 @@
         }
 
         @Override
+        public void toggleAndFollowTvPower() {
+            HdmiControlServiceWrapper.this.toggleAndFollowTvPower();
+        }
+
+        @Override
         public void queryDisplayStatus(IHdmiControlCallback callback) {
             HdmiControlServiceWrapper.this.queryDisplayStatus(callback);
         }
@@ -360,6 +365,9 @@
     public void oneTouchPlay(IHdmiControlCallback callback) {}
 
     /** @hide */
+    public void toggleAndFollowTvPower() {}
+
+    /** @hide */
     public void queryDisplayStatus(IHdmiControlCallback callback) {}
 
     /** @hide */
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index af9d3ac..6d0c688 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -42,6 +42,7 @@
     int[] getSupportedTypes();
     HdmiDeviceInfo getActiveSource();
     void oneTouchPlay(IHdmiControlCallback callback);
+    void toggleAndFollowTvPower();
     void queryDisplayStatus(IHdmiControlCallback callback);
     void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
     void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 923b9b76..81e6e78 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -81,8 +81,10 @@
     private final transient boolean mParcelSensitiveFields;
 
     private static final int MIN_MTU    = 68;
-    /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
-    static final int MIN_MTU_V6 = 1280;
+
+    /** @hide */
+    public static final int MIN_MTU_V6 = 1280;
+
     private static final int MAX_MTU    = 10000;
 
     private static final int INET6_ADDR_LENGTH = 16;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f806b56..40bb8bf 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -339,10 +339,14 @@
     public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
 
     /**
+     * Indicates that this network is temporarily unmetered.
+     * <p>
      * This capability will be set for networks that are generally metered, but are currently
      * unmetered, e.g., because the user is in a particular area. This capability can be changed at
      * any time. When it is removed, applications are responsible for stopping any data transfer
      * that should not occur on a metered network.
+     * Note that most apps should use {@link #NET_CAPABILITY_NOT_METERED} instead. For more
+     * information, see https://developer.android.com/about/versions/11/features/5g#meteredness.
      */
     public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
 
@@ -370,8 +374,8 @@
             | (1 << NET_CAPABILITY_FOREGROUND)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
-            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY
-            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -1802,20 +1806,26 @@
             sb.append(" OwnerUid: ").append(mOwnerUid);
         }
 
-        if (mAdministratorUids.length == 0) {
-            sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
+        if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+            sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
+        }
+
+        if (mRequestorUid != Process.INVALID_UID) {
+            sb.append(" RequestorUid: ").append(mRequestorUid);
+        }
+
+        if (mRequestorPackageName != null) {
+            sb.append(" RequestorPkg: ").append(mRequestorPackageName);
         }
 
         if (null != mSSID) {
             sb.append(" SSID: ").append(mSSID);
         }
 
-        if (mPrivateDnsBroken) {
-            sb.append(" Private DNS is broken");
-        }
 
-        sb.append(" RequestorUid: ").append(mRequestorUid);
-        sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+        if (mPrivateDnsBroken) {
+            sb.append(" PrivateDnsBroken");
+        }
 
         sb.append("]");
         return sb.toString();
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
new file mode 100644
index 0000000..8160edc
--- /dev/null
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -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.
+ */
+package android.net.vcn;
+
+import android.annotation.NonNull;
+
+/**
+ * This class represents a configuration for a connection to a Virtual Carrier Network gateway.
+ *
+ * <p>Each VcnGatewayConnectionConfig represents a single logical connection to a carrier gateway,
+ * and may provide one or more telephony services (as represented by network capabilities). Each
+ * gateway is expected to provide mobility for a given session as the device roams across {@link
+ * Network}s.
+ *
+ * <p>A VCN connection based on this configuration will be brought up dynamically based on device
+ * settings, and filed NetworkRequests. Underlying networks will be selected based on the services
+ * required by this configuration (as represented by network capabilities), and must be part of the
+ * subscription group under which this configuration is registered (see {@link
+ * VcnManager#setVcnConfig}).
+ *
+ * <p>Services that can be provided by a VCN network, or required for underlying networks are
+ * limited to services provided by cellular networks:
+ *
+ * <ul>
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MMS}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_SUPL}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_DUN}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_FOTA}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IMS}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_CBS}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IA}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_RCS}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_XCAP}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_EIMS}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET}
+ *   <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX}
+ * </ul>
+ *
+ * @hide
+ */
+public final class VcnGatewayConnectionConfig {
+    private VcnGatewayConnectionConfig() {
+        validate();
+    }
+
+    // TODO: Implement getters, validators, etc
+
+    /**
+     * Validates this configuration
+     *
+     * @hide
+     */
+    private void validate() {
+        // TODO: implement validation logic
+    }
+
+    // Parcelable methods
+
+    /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */
+    public static class Builder {
+        // TODO: Implement this builder
+
+        /**
+         * Builds and validates the VcnGatewayConnectionConfig
+         *
+         * @return an immutable VcnGatewayConnectionConfig instance
+         */
+        @NonNull
+        public VcnGatewayConnectionConfig build() {
+            return new VcnGatewayConnectionConfig();
+        }
+    }
+}
diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java
deleted file mode 100644
index 366da3d..0000000
--- a/core/java/android/os/BasicShellCommandHandler.java
+++ /dev/null
@@ -1,342 +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 android.os;
-
-import android.util.Log;
-
-import java.io.BufferedInputStream;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-
-/**
- * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. This is meant to
- * be copied into mainline modules, so this class must not use any hidden APIs.
- *
- * @hide
- */
-public abstract class BasicShellCommandHandler {
-    static final String TAG = "ShellCommand";
-    static final boolean DEBUG = false;
-
-    private Binder mTarget;
-    private FileDescriptor mIn;
-    private FileDescriptor mOut;
-    private FileDescriptor mErr;
-    private String[] mArgs;
-
-    private String mCmd;
-    private int mArgPos;
-    private String mCurArgData;
-
-    private FileInputStream mFileIn;
-    private FileOutputStream mFileOut;
-    private FileOutputStream mFileErr;
-
-    private PrintWriter mOutPrintWriter;
-    private PrintWriter mErrPrintWriter;
-    private InputStream mInputStream;
-
-    public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, int firstArgPos) {
-        mTarget = target;
-        mIn = in;
-        mOut = out;
-        mErr = err;
-        mArgs = args;
-        mCmd = null;
-        mArgPos = firstArgPos;
-        mCurArgData = null;
-        mFileIn = null;
-        mFileOut = null;
-        mFileErr = null;
-        mOutPrintWriter = null;
-        mErrPrintWriter = null;
-        mInputStream = null;
-    }
-
-    public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args) {
-        String cmd;
-        int start;
-        if (args != null && args.length > 0) {
-            cmd = args[0];
-            start = 1;
-        } else {
-            cmd = null;
-            start = 0;
-        }
-        init(target, in, out, err, args, start);
-        mCmd = cmd;
-
-        if (DEBUG) {
-            RuntimeException here = new RuntimeException("here");
-            here.fillInStackTrace();
-            Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here);
-            Log.d(TAG, "Calling uid=" + Binder.getCallingUid()
-                    + " pid=" + Binder.getCallingPid());
-        }
-        int res = -1;
-        try {
-            res = onCommand(mCmd);
-            if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget);
-        } catch (Throwable e) {
-            // Unlike usual calls, in this case if an exception gets thrown
-            // back to us we want to print it back in to the dump data, since
-            // that is where the caller expects all interesting information to
-            // go.
-            PrintWriter eout = getErrPrintWriter();
-            eout.println();
-            eout.println("Exception occurred while executing '" + mCmd + "':");
-            e.printStackTrace(eout);
-        } finally {
-            if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget);
-            if (mOutPrintWriter != null) {
-                mOutPrintWriter.flush();
-            }
-            if (mErrPrintWriter != null) {
-                mErrPrintWriter.flush();
-            }
-            if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget);
-        }
-        if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget);
-        return res;
-    }
-
-    /**
-     * Return the raw FileDescriptor for the output stream.
-     */
-    public FileDescriptor getOutFileDescriptor() {
-        return mOut;
-    }
-
-    /**
-     * Return direct raw access (not buffered) to the command's output data stream.
-     */
-    public OutputStream getRawOutputStream() {
-        if (mFileOut == null) {
-            mFileOut = new FileOutputStream(mOut);
-        }
-        return mFileOut;
-    }
-
-    /**
-     * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}.
-     */
-    public PrintWriter getOutPrintWriter() {
-        if (mOutPrintWriter == null) {
-            mOutPrintWriter = new PrintWriter(getRawOutputStream());
-        }
-        return mOutPrintWriter;
-    }
-
-    /**
-     * Return the raw FileDescriptor for the error stream.
-     */
-    public FileDescriptor getErrFileDescriptor() {
-        return mErr;
-    }
-
-    /**
-     * Return direct raw access (not buffered) to the command's error output data stream.
-     */
-    public OutputStream getRawErrorStream() {
-        if (mFileErr == null) {
-            mFileErr = new FileOutputStream(mErr);
-        }
-        return mFileErr;
-    }
-
-    /**
-     * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}.
-     */
-    public PrintWriter getErrPrintWriter() {
-        if (mErr == null) {
-            return getOutPrintWriter();
-        }
-        if (mErrPrintWriter == null) {
-            mErrPrintWriter = new PrintWriter(getRawErrorStream());
-        }
-        return mErrPrintWriter;
-    }
-
-    /**
-     * Return the raw FileDescriptor for the input stream.
-     */
-    public FileDescriptor getInFileDescriptor() {
-        return mIn;
-    }
-
-    /**
-     * Return direct raw access (not buffered) to the command's input data stream.
-     */
-    public InputStream getRawInputStream() {
-        if (mFileIn == null) {
-            mFileIn = new FileInputStream(mIn);
-        }
-        return mFileIn;
-    }
-
-    /**
-     * Return buffered access to the command's {@link #getRawInputStream()}.
-     */
-    public InputStream getBufferedInputStream() {
-        if (mInputStream == null) {
-            mInputStream = new BufferedInputStream(getRawInputStream());
-        }
-        return mInputStream;
-    }
-
-    /**
-     * Return the next option on the command line -- that is an argument that
-     * starts with '-'.  If the next argument is not an option, null is returned.
-     */
-    public String getNextOption() {
-        if (mCurArgData != null) {
-            String prev = mArgs[mArgPos - 1];
-            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
-        }
-        if (mArgPos >= mArgs.length) {
-            return null;
-        }
-        String arg = mArgs[mArgPos];
-        if (!arg.startsWith("-")) {
-            return null;
-        }
-        mArgPos++;
-        if (arg.equals("--")) {
-            return null;
-        }
-        if (arg.length() > 1 && arg.charAt(1) != '-') {
-            if (arg.length() > 2) {
-                mCurArgData = arg.substring(2);
-                return arg.substring(0, 2);
-            } else {
-                mCurArgData = null;
-                return arg;
-            }
-        }
-        mCurArgData = null;
-        return arg;
-    }
-
-    /**
-     * Return the next argument on the command line, whatever it is; if there are
-     * no arguments left, return null.
-     */
-    public String getNextArg() {
-        if (mCurArgData != null) {
-            String arg = mCurArgData;
-            mCurArgData = null;
-            return arg;
-        } else if (mArgPos < mArgs.length) {
-            return mArgs[mArgPos++];
-        } else {
-            return null;
-        }
-    }
-
-    public String peekNextArg() {
-        if (mCurArgData != null) {
-            return mCurArgData;
-        } else if (mArgPos < mArgs.length) {
-            return mArgs[mArgPos];
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @return all the remaining arguments in the command without moving the current position.
-     */
-    public String[] peekRemainingArgs() {
-        int remaining = getRemainingArgsCount();
-        String[] args = new String[remaining];
-        for (int pos = mArgPos; pos < mArgs.length; pos++) {
-            args[pos - mArgPos] = mArgs[pos];
-        }
-        return args;
-    }
-
-    /**
-     * Returns number of arguments that haven't been processed yet.
-     */
-    public int getRemainingArgsCount() {
-        if (mArgPos >= mArgs.length) {
-            return 0;
-        }
-        return mArgs.length - mArgPos;
-    }
-
-    /**
-     * Return the next argument on the command line, whatever it is; if there are
-     * no arguments left, throws an IllegalArgumentException to report this to the user.
-     */
-    public String getNextArgRequired() {
-        String arg = getNextArg();
-        if (arg == null) {
-            String prev = mArgs[mArgPos - 1];
-            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
-        }
-        return arg;
-    }
-
-    public int handleDefaultCommands(String cmd) {
-        if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
-            onHelp();
-        } else {
-            getOutPrintWriter().println("Unknown command: " + cmd);
-        }
-        return -1;
-    }
-
-    public Binder getTarget() {
-        return mTarget;
-    }
-
-    public String[] getAllArgs() {
-        return mArgs;
-    }
-
-    /**
-     * Implement parsing and execution of a command.  If it isn't a command you understand,
-     * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
-     * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
-     * to process additional command line arguments.  Command output can be written to
-     * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
-     *
-     * <p class="caution">Note that no permission checking has been done before entering this
-     * function, so you need to be sure to do your own security verification for any commands you
-     * are executing.  The easiest way to do this is to have the ShellCommand contain
-     * only a reference to your service's aidl interface, and do all of your command
-     * implementations on top of that -- that way you can rely entirely on your executing security
-     * code behind that interface.</p>
-     *
-     * @param cmd The first command line argument representing the name of the command to execute.
-     * @return Return the command result; generally 0 or positive indicates success and
-     * negative values indicate error.
-     */
-    public abstract int onCommand(String cmd);
-
-    /**
-     * Implement this to print help text about your command to {@link #getOutPrintWriter()}.
-     */
-    public abstract void onHelp();
-}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 3358ce1..a2173a6 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,15 +19,9 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Slog;
 
-import com.android.internal.util.FastPrintWriter;
+import com.android.modules.utils.BasicShellCommandHandler;
 
-import java.io.BufferedInputStream;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
 
 /**
  * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index c89adad..086180e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -85,8 +85,6 @@
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -1045,22 +1043,22 @@
             /**
              * Detect attempts to invoke a method on a {@link Context} that is not suited for such
              * operation.
-             * <p>An example of this is trying to obtain an instance of visual service (e.g.
+             * <p>An example of this is trying to obtain an instance of UI service (e.g.
              * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
              * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
              * therefore can report incorrect metrics or resources.
              * @see Context#getDisplay()
              * @see Context#getSystemService(String)
-             * @hide
              */
-            @TestApi
             public @NonNull Builder detectIncorrectContextUse() {
                 return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
             }
 
             /**
              * Disable detection of incorrect context use.
-             * TODO(b/149790106): Fix usages and remove.
+             *
+             * @see #detectIncorrectContextUse()
+             *
              * @hide
              */
             @TestApi
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 3b1ef25..c34d6e8 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -18,7 +18,7 @@
 public final class UntaggedSocketViolation extends Violation {
     /** @hide */
     public UntaggedSocketViolation() {
-        super("Untagged socket detected; use TrafficStats.setThreadSocketTag() to "
+        super("Untagged socket detected; use TrafficStats.setTrafficStatsTag() to "
                 + "track all network usage");
     }
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 97c9f4b..714bcea 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -457,6 +457,20 @@
      */
     public static final String NAMESPACE_CONFIGURATION = "configuration";
 
+    /**
+     * LatencyTracker properties definitions.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_LATENCY_TRACKER = "latency_tracker";
+
+    /**
+     * InteractionJankMonitor properties definitions.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_INTERACTION_JANK_MONITOR = "interaction_jank_monitor";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index 8b7d6ad..97e0156 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,4 +1,5 @@
 per-file DeviceConfig.java = svetoslavganov@google.com
 per-file DeviceConfig.java = hackbod@google.com
+per-file DeviceConfig.java = schfan@google.com
 
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3e48168..884f8cc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8412,6 +8412,14 @@
                 "emergency_gesture_sound_enabled";
 
         /**
+         * The default number to call in emergency gesture
+         *
+         * @hide
+         */
+        public static final String EMERGENCY_GESTURE_CALL_NUMBER =
+                "emergency_gesture_call_number";
+
+        /**
          * Whether the camera launch gesture to double tap the power button when the screen is off
          * should be disabled.
          *
@@ -14576,19 +14584,6 @@
          */
         public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
                 "maximum_obscuring_opacity_for_touch";
-
-        /**
-         * LatencyTracker settings.
-         *
-         * The following strings are supported as keys:
-         * <pre>
-         *     enabled              (boolean)
-         *     sampling_interval    (int)
-         * </pre>
-         *
-         * @hide
-         */
-        public static final String LATENCY_TRACKER = "latency_tracker";
     }
 
     /**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 649c8f3..83cab0a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5169,6 +5169,14 @@
         public static final String COLUMN_IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
 
         /**
+         * TelephonyProvider column name for determining if the user has enabled cross SIM calling
+         * for this subscription.
+         *
+         * @hide
+         */
+        public static final String COLUMN_CROSS_SIM_CALLING_ENABLED = "cross_sim_calling_enabled";
+
+        /**
          * TelephonyProvider column name for whether a subscription is opportunistic, that is,
          * whether the network it connects to is limited in functionality or coverage.
          * For example, CBRS.
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/attestation/IImpressionAttestationService.aidl
index 8e858b8..fcbc51f 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/attestation/IImpressionAttestationService.aidl
@@ -18,8 +18,8 @@
 
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
-import android.service.attestation.ImpressionToken;
 import android.os.RemoteCallback;
+import android.service.attestation.ImpressionToken;
 
 /**
  * Service used to handle impression attestation requests.
@@ -31,22 +31,26 @@
      * Generates the impression token that can be used to validate that the system generated the
      * token.
      *
-     * @param screenshot The token for the window where the view is shown.
+     * @param salt The salt to use when generating the hmac. This should be unique to the caller so
+     *        the token cannot be verified by any other process.
+     * @param screenshot The screenshot to generate the hash and add to the token.
      * @param bounds The size and position of the content being attested in the window.
      * @param hashAlgorithm The String for the hashing algorithm to use based on values in
      *        {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
      * @param Callback The callback invoked to send back the impression token.
      */
-    void generateImpressionToken(in HardwareBuffer screenshot, in Rect bounds,
+    void generateImpressionToken(in String salt, in HardwareBuffer screenshot, in Rect bounds,
                                  in String hashAlgorithm, in RemoteCallback callback);
 
     /**
      * Call to verify that the impressionToken passed in was generated by the system. The result
-     * will be sent in the callback as an integer with the key {@link #EXTRA_VERIFICATION_STATUS}
-     * and will be one of the values in {@link VerificationStatus}.
+     * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
      *
+     * @param salt The salt value to use when verifying the hmac. This should be the same value that
+     *        was passed to {@link generateImpressionToken()} to generate the token.
      * @param impressionToken The token to verify that it was generated by the system.
      * @param callback The callback invoked to send back the verification status.
      */
-    void verifyImpressionToken(in ImpressionToken impressionToken, in RemoteCallback callback);
+    void verifyImpressionToken(in String salt, in ImpressionToken impressionToken,
+                               in RemoteCallback callback);
 }
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
index 4919f5d..05ad5f0 100644
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ b/core/java/android/service/attestation/ImpressionAttestationService.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -50,27 +49,24 @@
     public static final String EXTRA_VERIFICATION_STATUS =
             "android.service.attestation.extra.VERIFICATION_STATUS";
 
-    /** @hide */
-    @IntDef(prefix = {"VERIFICATION_STATUS_"}, value = {
-            VERIFICATION_STATUS_UNKNOWN,
-            VERIFICATION_STATUS_OS_VERIFIED,
-            VERIFICATION_STATUS_APP_DECLARED
-    })
-    public @interface VerificationStatus {
-    }
-
-    public static final int VERIFICATION_STATUS_UNKNOWN = 0;
-    public static final int VERIFICATION_STATUS_OS_VERIFIED = 1;
-    public static final int VERIFICATION_STATUS_APP_DECLARED = 2;
-
     /**
      * Manifest metadata key for the resource string array containing the names of all impression
      * attestation algorithms provided by the service.
+     *
      * @hide
      */
     public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
             "android.attestation.available_algorithms";
 
+    /**
+     * The {@link Intent} action that must be declared as handled by a service in its manifest
+     * for the system to recognize it as an impression attestation providing service.
+     *
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.attestation.ImpressionAttestationService";
+
     private ImpressionAttestationServiceWrapper mWrapper;
     private Handler mHandler;
 
@@ -94,6 +90,8 @@
      * Generates the impression token that can be used to validate that the system
      * generated the token.
      *
+     * @param salt          The salt to use when generating the hmac. This should be unique to the
+     *                      caller so the token cannot be verified by any other process.
      * @param screenshot    The screenshot buffer for the content to attest.
      * @param bounds        The size and position of the content being attested in the window.
      * @param hashAlgorithm The String for the hashing algorithm to use based values in
@@ -102,51 +100,57 @@
      * Returns null when the arguments sent are invalid.
      */
     @Nullable
-    public abstract ImpressionToken onGenerateImpressionToken(@NonNull HardwareBuffer screenshot,
-            @NonNull Rect bounds, @NonNull String hashAlgorithm);
+    public abstract ImpressionToken onGenerateImpressionToken(@NonNull String salt,
+            @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
+            @NonNull String hashAlgorithm);
 
     /**
      * Call to verify that the impressionToken passed in was generated by the system.
      *
+     * @param salt            The salt value to use when verifying the hmac. This should be the
+     *                        same value that was passed to
+     *                        {@link #onGenerateImpressionToken(String,
+     *                        HardwareBuffer, Rect, String)} to
+     *                        generate the token.
      * @param impressionToken The token to verify that it was generated by the system.
-     * @return A {@link VerificationStatus} about whether the token was generated by the system.
+     * @return true if the token can be verified that it was generated by the system.
      */
-    public abstract @VerificationStatus int onVerifyImpressionToken(
+    public abstract boolean onVerifyImpressionToken(@NonNull String salt,
             @NonNull ImpressionToken impressionToken);
 
-    private void generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+    private void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds,
             String hashAlgorithm, RemoteCallback callback) {
-        ImpressionToken impressionToken = onGenerateImpressionToken(screenshot, bounds,
+        ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
                 hashAlgorithm);
         final Bundle data = new Bundle();
         data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken);
         callback.sendResult(data);
     }
 
-    private void verifyImpressionToken(ImpressionToken impressionToken,
+    private void verifyImpressionToken(String salt, ImpressionToken impressionToken,
             RemoteCallback callback) {
-        @VerificationStatus int verificationStatus = onVerifyImpressionToken(impressionToken);
+        boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
         final Bundle data = new Bundle();
-        data.putInt(EXTRA_VERIFICATION_STATUS, verificationStatus);
+        data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
         callback.sendResult(data);
     }
 
     private final class ImpressionAttestationServiceWrapper extends
             IImpressionAttestationService.Stub {
         @Override
-        public void generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+        public void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds,
                 String hashAlgorithm, RemoteCallback callback) {
             mHandler.sendMessage(
                     obtainMessage(ImpressionAttestationService::generateImpressionToken,
-                            ImpressionAttestationService.this, screenshot, bounds, hashAlgorithm,
-                            callback));
+                            ImpressionAttestationService.this, salt, screenshot, bounds,
+                            hashAlgorithm, callback));
         }
 
         @Override
-        public void verifyImpressionToken(ImpressionToken impressionToken,
+        public void verifyImpressionToken(String salt, ImpressionToken impressionToken,
                 RemoteCallback callback) {
             mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
-                    ImpressionAttestationService.this, impressionToken, callback));
+                    ImpressionAttestationService.this, salt, impressionToken, callback));
         }
     }
 }
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index b4b5819..53290e2 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -176,6 +176,9 @@
 
     @Override
     public void onDestroy() {
+        // As this call will come asynchronously in the main thread, prevent calls from the binder
+        // being processed after this.
+        mHandler.removeCallbacksAndMessages(null);
         if (mListening) {
             onStopListening();
             mListening = false;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bfa3123..479a0c1 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -17,10 +17,7 @@
 package android.telephony;
 
 import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
@@ -31,27 +28,17 @@
 import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.DataActivityType;
-import android.telephony.Annotation.DisconnectCauses;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
-import android.telephony.NetworkRegistrationInfo.Domain;
-import android.telephony.TelephonyManager.DataEnabledReason;
-import android.telephony.TelephonyManager.DataState;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IPhoneStateListener;
 
 import dalvik.system.VMRuntime;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Map;
@@ -126,9 +113,7 @@
      *
      *  @see #onServiceStateChanged
      *  @see ServiceState
-     *  @deprecated Use {@link ServiceStateChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_SERVICE_STATE                            = 0x00000001;
 
     /**
@@ -136,7 +121,8 @@
      * {@more}
      *
      * @see #onSignalStrengthChanged
-     * @deprecated Use {@link SignalStrengthsChangedListener} instead.
+     *
+     * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS}
      */
     @Deprecated
     public static final int LISTEN_SIGNAL_STRENGTH                          = 0x00000002;
@@ -152,9 +138,7 @@
      * voicemail icon.
      *
      * @see #onMessageWaitingIndicatorChanged
-     * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_MESSAGE_WAITING_INDICATOR                = 0x00000004;
 
     /**
@@ -165,9 +149,7 @@
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onCallForwardingIndicatorChanged
-     * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
 
     /**
@@ -183,9 +165,7 @@
      * instead.
      *
      * @see #onCellLocationChanged
-     * @deprecated Use {@link CellLocationChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_CELL_LOCATION                            = 0x00000010;
 
     /**
@@ -193,18 +173,14 @@
      * {@more}
      *
      * @see #onCallStateChanged
-     * @deprecated Use {@link CallStateChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_CALL_STATE                               = 0x00000020;
 
     /**
      * Listen for changes to the data connection state (cellular).
      *
      * @see #onDataConnectionStateChanged
-     * @deprecated Use {@link DataConnectionStateChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_DATA_CONNECTION_STATE                    = 0x00000040;
 
     /**
@@ -215,9 +191,7 @@
      * data-traffic icon.
      *
      * @see #onDataActivity
-     * @deprecated Use {@link DataActivityListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_DATA_ACTIVITY                            = 0x00000080;
 
     /**
@@ -227,9 +201,7 @@
      * icon.
      *
      * @see #onSignalStrengthsChanged
-     * @deprecated Use {@link SignalStrengthsChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
     /**
@@ -239,9 +211,7 @@
      * @see #onSignalStrengthsChanged
      *
      * @hide
-     * @deprecated Use {@link AlwaysReportedSignalStrengthsChangedListener} instead.
      */
-    @Deprecated
     @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
     public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH          = 0x00000200;
 
@@ -252,9 +222,7 @@
      * permission.
      *
      * @see #onCellInfoChanged
-     * @deprecated Use {@link CellInfoChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_CELL_INFO = 0x00000400;
 
     /**
@@ -266,10 +234,8 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @hide
-     * @deprecated Use {@link PreciseCallStateChangedListener} instead.
      */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
 
@@ -281,10 +247,8 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onPreciseDataConnectionStateChanged
-     * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead.
      */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
 
     /**
@@ -294,7 +258,7 @@
      * READ_PRECISE_PHONE_STATE}
      * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
      *
-     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead.
+     * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
      * @hide
      */
     @Deprecated
@@ -307,9 +271,7 @@
      *
      * @see #onServiceStateChanged(ServiceState)
      * @hide
-     * @deprecated Use {@link SrvccStateChangedListener} instead.
      */
-    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public static final int LISTEN_SRVCC_STATE_CHANGED                     = 0x00004000;
@@ -327,11 +289,10 @@
     /**
      * Listen for carrier network changes indicated by a carrier app.
      *
-     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+     * @see #onCarrierNetworkRequest
+     * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
      * @hide
-     * @deprecated Use {@link CarrierNetworkChangeListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
 
     /**
@@ -350,9 +311,7 @@
      *
      * @see #onVoiceActivationStateChanged
      * @hide
-     * @deprecated Use {@link VoiceActivationStateChangedListener} instead.
      */
-    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public static final int LISTEN_VOICE_ACTIVATION_STATE                   = 0x00020000;
@@ -364,24 +323,20 @@
      * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
      * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
      * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *
+     * {@more}
      * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
      * fully activated
      *
      * @see #onDataActivationStateChanged
      * @hide
-     * @deprecated Use {@link DataActivationStateChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
 
     /**
      *  Listen for changes to the user mobile data state
      *
      *  @see #onUserMobileDataStateChanged
-     *  @deprecated Use {@link UserMobileDataStateChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
 
     /**
@@ -392,9 +347,7 @@
      *  {@link TelephonyManager#hasCarrierPrivileges}).
      *
      *  @see #onDisplayInfoChanged
-     * @deprecated Use {@link DisplayInfoChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
 
     /**
@@ -402,9 +355,7 @@
      *
      *  @see #onPhoneCapabilityChanged
      *  @hide
-     *  @deprecated Use {@link PhoneCapabilityChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_PHONE_CAPABILITY_CHANGE                 = 0x00200000;
 
     /**
@@ -414,19 +365,17 @@
      *  subscription user selected as default data subscription in DSDS mode.
      *
      *  @see #onActiveDataSubscriptionIdChanged
-     *  @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
 
     /**
      *  Listen for changes to the radio power state.
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
      *  @see #onRadioPowerStateChanged
      *  @hide
-     *  @deprecated Use {@link RadioPowerStateChangedListener} instead.
      */
-    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public static final int LISTEN_RADIO_POWER_STATE_CHANGED               = 0x00800000;
@@ -436,10 +385,7 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @deprecated Use {@link EmergencyNumberListChangedListener} instead.
      */
-    @Deprecated
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
     /**
@@ -450,10 +396,8 @@
      * or the calling app has carrier privileges
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @deprecated Use {@link CallDisconnectCauseChangedListener} instead.
      */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
     /**
@@ -465,11 +409,9 @@
      *
      * @see #onCallAttributesChanged
      * @hide
-     * @deprecated Use {@link CallAttributesChangedListener} instead.
      */
-    @Deprecated
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
 
     /**
@@ -481,20 +423,18 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
-     * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead.
      */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES              = 0x08000000;
 
     /**
      * Listen for the emergency number placed from an outgoing call.
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
      * @see #onOutgoingEmergencyCall
      * @hide
-     * @deprecated Use {@link OutgoingEmergencyCallListener} instead.
      */
-    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_EMERGENCY_CALL                  = 0x10000000;
@@ -502,11 +442,11 @@
     /**
      * Listen for the emergency number placed from an outgoing SMS.
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
      * @see #onOutgoingEmergencySms
      * @hide
-     * @deprecated Use {@link OutgoingEmergencySmsListener} instead.
      */
-    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_EMERGENCY_SMS                   = 0x20000000;
@@ -525,9 +465,7 @@
      * of whether the calling app has carrier privileges.
      *
      * @see #onRegistrationFailed
-     * @deprecated Use {@link RegistrationFailedListener} instead.
      */
-    @Deprecated
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
@@ -541,525 +479,19 @@
      * of whether the calling app has carrier privileges.
      *
      * @see #onBarringInfoChanged
-     * @deprecated Use {@link BarringInfoChangedListener} instead.
      */
-    @Deprecated
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
     /**
-     *  Event for changes to the network service state (cellular).
+     *  Listen for changes to the physical channel configuration.
      *
-     *  @see ServiceStateChangedListener#onServiceStateChanged
-     *  @see ServiceState
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SERVICE_STATE_CHANGED = 1;
-
-    /**
-     * Event for changes to the network signal strength (cellular).
-     *
-     * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
-
-    /**
-     * Event for changes to the message-waiting indicator.
-     *
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
-     * the calling app has carrier privileges (see
-     * {@link TelephonyManager#hasCarrierPrivileges}).
-     * <p>
-     * Example: The status bar uses this to determine when to display the
-     * voicemail icon.
-     *
-     * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
-
-    /**
-     * Event for changes to the call-forwarding indicator.
-     *
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
-     * the calling app has carrier privileges (see
-     * {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
-
-    /**
-     * Event for changes to the device's cell location. Note that
-     * this will result in frequent callbacks to the listener.
-     *
-     * If you need regular location updates but want more control over
-     * the update interval or location precision, you can set up a listener
-     * through the {@link android.location.LocationManager location manager}
-     * instead.
-     *
-     * @see CellLocationChangedListener#onCellLocationChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_LOCATION_CHANGED = 5;
-
-    /**
-     * Event for changes to the device call state.
-     *
-     * @see CallStateChangedListener#onCallStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
-    public static final int EVENT_CALL_STATE_CHANGED = 6;
-
-    /**
-     * Event for changes to the data connection state (cellular).
-     *
-     * @see DataConnectionStateChangedListener#onDataConnectionStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
-
-    /**
-     * Event for changes to the direction of data traffic on the data
-     * connection (cellular).
-     *
-     * Example: The status bar uses this to display the appropriate
-     * data-traffic icon.
-     *
-     * @see DataActivityListener#onDataActivity
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
-
-    /**
-     * Event for changes to the network signal strengths (cellular).
-     * <p>
-     * Example: The status bar uses this to control the signal-strength
-     * icon.
-     *
-     * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
-
-    /**
-     * Event for changes of the network signal strengths (cellular) always reported from modem,
-     * even in some situations such as the screen of the device is off.
-     *
-     * @see AlwaysReportedSignalStrengthsChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-    public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
-
-    /**
-     * Event for changes to observed cell info.
-     *
-     * @see CellInfoChangedListener#onCellInfoChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_INFO_CHANGED = 11;
-
-    /**
-     * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
-     * background and foreground calls.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PreciseCallStateChangedListener#onPreciseCallStateChanged
-     *
+     * @see #onPhysicalChannelConfigurationChanged
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
-
-    /**
-     * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
-
-    /**
-     * Event for real time info for all data connections (cellular)).
-     *
-     * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
-     *
-     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
-     * @hide
-     */
-    @Deprecated
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
-
-    /**
-     * Event for OEM hook raw event
-     *
-     * @see #onOemHookRawEvent
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_OEM_HOOK_RAW = 15;
-
-    /**
-     * Event for changes to the SRVCC state of the active call.
-     *
-     * @see SrvccStateChangedListener#onSrvccStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_SRVCC_STATE_CHANGED = 16;
-
-    /**
-     * Event for carrier network changes indicated by a carrier app.
-     *
-     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
-     * @see CarrierNetworkChangeListener#onCarrierNetworkChange
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
-
-    /**
-     * Event for changes to the sim voice activation state
-     *
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *
-     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
-     * fully activated
-     *
-     * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
-
-    /**
-     * Event for changes to the sim data activation state
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *
-     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
-     * fully activated
-     *
-     * @see DataActivationStateChangedListener#onDataActivationStateChanged
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
-
-    /**
-     *  Event for changes to the user mobile data state
-     *
-     *  @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
-
-    /**
-     *  Event for display info changed event.
-     *
-     *  @see DisplayInfoChangedListener#onDisplayInfoChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
-
-    /**
-     *  Event for changes to the phone capability.
-     *
-     *  @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
-
-    /**
-     *  Event for changes to active data subscription ID. Active data subscription is
-     *  the current subscription used to setup Cellular Internet data. For example,
-     *  it could be the current active opportunistic subscription in use, or the
-     *  subscription user selected as default data subscription in DSDS mode.
-     *
-     *  <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     *  app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
-
-    /**
-     *  Event for changes to the radio power state.
-     *
-     *  @see RadioPowerStateChangedListener#onRadioPowerStateChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
-
-    /**
-     * Event for changes to emergency number list based on all active subscriptions.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
-
-    /**
-     * Event for call disconnect causes which contains {@link DisconnectCause} and
-     * {@link PreciseDisconnectCause}.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
-
-    /**
-     * Event for changes to the call attributes of a currently active call.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see CallAttributesChangedListener#onCallAttributesChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
-
-    /**
-     * Event for IMS call disconnect causes which contains
-     * {@link android.telephony.ims.ImsReasonInfo}
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
-
-    /**
-     * Event for the emergency number placed from an outgoing call.
-     *
-     * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
-
-    /**
-     * Event for the emergency number placed from an outgoing SMS.
-     *
-     * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
-
-    /**
-     * Event for registration failures.
-     *
-     * Event for indications that a registration procedure has failed in either the CS or PS
-     * domain. This indication does not necessarily indicate a change of service state, which should
-     * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
-     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
-     * of whether the calling app has carrier privileges.
-     *
-     * @see RegistrationFailedListener#onRegistrationFailed
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            Manifest.permission.READ_PRECISE_PHONE_STATE,
-            Manifest.permission.ACCESS_FINE_LOCATION
-    })
-    public static final int EVENT_REGISTRATION_FAILURE = 31;
-
-    /**
-     * Event for Barring Information for the current registered / camped cell.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
-     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
-     * of whether the calling app has carrier privileges.
-     *
-     * @see BarringInfoChangedListener#onBarringInfoChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            Manifest.permission.READ_PRECISE_PHONE_STATE,
-            Manifest.permission.ACCESS_FINE_LOCATION
-    })
-    public static final int EVENT_BARRING_INFO_CHANGED = 32;
-
-    /**
-     * Event for changes to the physical channel configuration.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
-
-
-    /**
-     * Event for changes to the data enabled.
-     *
-     * Event for indications that the enabled status of current data has changed.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see DataEnabledChangedListener#onDataEnabledChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_DATA_ENABLED_CHANGED = 34;
-
-    /** @hide */
-    @IntDef(prefix = { "EVENT_" }, value = {
-            EVENT_SERVICE_STATE_CHANGED,
-            EVENT_SIGNAL_STRENGTH_CHANGED,
-            EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
-            EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
-            EVENT_CELL_LOCATION_CHANGED,
-            EVENT_CALL_STATE_CHANGED,
-            EVENT_DATA_CONNECTION_STATE_CHANGED,
-            EVENT_DATA_ACTIVITY_CHANGED,
-            EVENT_SIGNAL_STRENGTHS_CHANGED,
-            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
-            EVENT_CELL_INFO_CHANGED,
-            EVENT_PRECISE_CALL_STATE_CHANGED,
-            EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
-            EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
-            EVENT_OEM_HOOK_RAW,
-            EVENT_SRVCC_STATE_CHANGED,
-            EVENT_CARRIER_NETWORK_CHANGED,
-            EVENT_VOICE_ACTIVATION_STATE_CHANGED,
-            EVENT_DATA_ACTIVATION_STATE_CHANGED,
-            EVENT_USER_MOBILE_DATA_STATE_CHANGED,
-            EVENT_DISPLAY_INFO_CHANGED,
-            EVENT_PHONE_CAPABILITY_CHANGED,
-            EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
-            EVENT_RADIO_POWER_STATE_CHANGED,
-            EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
-            EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
-            EVENT_CALL_ATTRIBUTES_CHANGED,
-            EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
-            EVENT_OUTGOING_EMERGENCY_CALL,
-            EVENT_OUTGOING_EMERGENCY_SMS,
-            EVENT_REGISTRATION_FAILURE,
-            EVENT_BARRING_INFO_CHANGED,
-            EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
-            EVENT_DATA_ENABLED_CHANGED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TelephonyEvent {}
+    public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x100000000L;
 
     /*
      * Subscription used to listen to the phone state changes
@@ -1072,13 +504,9 @@
     /**
      * @hide
      */
-    //TODO: The maxTargetSdk should be S if the build time tool updates it.
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    @UnsupportedAppUsage(
-            maxTargetSdk = Build.VERSION_CODES.R,
-            publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" +
-                    "Executor, PhoneStateListener)} instead")
-    public IPhoneStateListener callback;
+    @UnsupportedAppUsage
+    public final IPhoneStateListener callback;
 
     /**
      * Create a PhoneStateListener for the Phone with the default subscription.
@@ -1135,737 +563,17 @@
      * The Executor must not be null.
      *
      * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
-     * @deprecated Use
-     * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead.
      */
-    @Deprecated
     public PhoneStateListener(@NonNull Executor executor) {
         this(null, executor);
     }
 
-    /**
-     * @hide
-     */
-    public void setExecutor(@NonNull @CallbackExecutor Executor executor) {
-        if (executor == null) {
+    private PhoneStateListener(Integer subId, Executor e) {
+        if (e == null) {
             throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
         }
-        callback = new IPhoneStateListenerStub(this, executor);
-    }
-
-    private PhoneStateListener(Integer subId, Executor e) {
-        setExecutor(e);
         mSubId = subId;
-    }
-
-    /**
-     * Interface for service state listener.
-     */
-    public interface ServiceStateChangedListener {
-        /**
-         * Callback invoked when device service state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * The instance of {@link ServiceState} passed as an argument here will have various
-         * levels of location information stripped from it depending on the location permissions
-         * that your app holds.
-         * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
-         * receive all the information in {@link ServiceState}.
-         *
-         * @see ServiceState#STATE_EMERGENCY_ONLY
-         * @see ServiceState#STATE_IN_SERVICE
-         * @see ServiceState#STATE_OUT_OF_SERVICE
-         * @see ServiceState#STATE_POWER_OFF
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onServiceStateChanged(@NonNull ServiceState serviceState);
-    }
-
-    /**
-     * Interface for message waiting indicator listener.
-     */
-    public interface MessageWaitingIndicatorChangedListener {
-        /**
-         * Callback invoked when the message-waiting indicator changes on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onMessageWaitingIndicatorChanged(boolean mwi);
-    }
-
-    /**
-     * Interface for call-forwarding indicator listener.
-     */
-    public interface CallForwardingIndicatorChangedListener {
-        /**
-         * Callback invoked when the call-forwarding indicator changes on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallForwardingIndicatorChanged(boolean cfi);
-    }
-
-    /**
-     * Interface for device cell location listener.
-     */
-    public interface CellLocationChangedListener {
-        /**
-         * Callback invoked when device cell location changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellLocationChanged(@NonNull CellLocation location);
-    }
-
-    /**
-     * Interface for call state listener.
-     */
-    public interface CallStateChangedListener {
-        /**
-         * Callback invoked when device call state changes.
-         * <p>
-         * Reports the state of Telephony (mobile) calls on the device for the registered s
-         * ubscription.
-         * <p>
-         * Note: the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         * <p>
-         * Note: The state returned here may differ from that returned by
-         * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
-         * calling {@link TelephonyManager#getCallState()} from within this callback may return a
-         * different state than the callback reports.
-         *
-         * @param state call state
-         * @param phoneNumber call phone number. If application does not have
-         * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier
-         * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
-         * passed as an argument.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
-        public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber);
-    }
-
-    /**
-     * Interface for data connection state listener.
-     */
-    public interface DataConnectionStateChangedListener {
-        /**
-         * Callback invoked when connection state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @see TelephonyManager#DATA_DISCONNECTED
-         * @see TelephonyManager#DATA_CONNECTING
-         * @see TelephonyManager#DATA_CONNECTED
-         * @see TelephonyManager#DATA_SUSPENDED
-         *
-         * @param state is the current state of data connection.
-         * @param networkType is the current network type of data connection.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataConnectionStateChanged(@DataState int state,
-                                                 @NetworkType int networkType);
-    }
-
-    /**
-     * Interface for data activity state listener.
-     */
-    public interface DataActivityListener {
-        /**
-         * Callback invoked when data activity state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @see TelephonyManager#DATA_ACTIVITY_NONE
-         * @see TelephonyManager#DATA_ACTIVITY_IN
-         * @see TelephonyManager#DATA_ACTIVITY_OUT
-         * @see TelephonyManager#DATA_ACTIVITY_INOUT
-         * @see TelephonyManager#DATA_ACTIVITY_DORMANT
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivity(@DataActivityType int direction);
-    }
-
-    /**
-     * Interface for network signal strengths listener.
-     */
-    public interface SignalStrengthsChangedListener {
-        /**
-         * Callback invoked when network signal strengths changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for network signal strengths listener which always reported from modem.
-     */
-    public interface AlwaysReportedSignalStrengthsChangedListener {
-        /**
-         * Callback always invoked from modem when network signal strengths changes on the
-         * registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for cell info listener.
-     */
-    public interface CellInfoChangedListener {
-        /**
-         * Callback invoked when a observed cell info has changed or new cells have been added
-         * or removed on the registered subscription.
-         * Note, the registration subscription ID s from {@link TelephonyManager} object
-         * which registersPhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param cellInfo is the list of currently visible cells.
-         */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
-    }
-
-    /**
-     * Interface for precise device call state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface PreciseCallStateChangedListener {
-        /**
-         * Callback invoked when precise device call state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param callState {@link PreciseCallState}
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
-    }
-
-    /**
-     * Interface for call disconnect cause listener.
-     */
-    public interface CallDisconnectCauseChangedListener {
-        /**
-         * Callback invoked when call disconnect cause changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param disconnectCause {@link DisconnectCause}.
-         * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
-                @PreciseDisconnectCauses int preciseDisconnectCause);
-    }
-
-    /**
-     * Interface for IMS call disconnect cause listener.
-     */
-    public interface ImsCallDisconnectCauseChangedListener {
-        /**
-         * Callback invoked when IMS call disconnect cause changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
-         *
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
-    }
-
-    /**
-     * Interface for precise data connection state listener.
-     */
-    public interface PreciseDataConnectionStateChangedListener {
-        /**
-         * Callback providing update about the default/internet data connection on the registered
-         * subscription.
-         *
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-         * or the calling app has carrier privileges
-         * (see {@link TelephonyManager#hasCarrierPrivileges}).
-         *
-         * @param dataConnectionState {@link PreciseDataConnectionState}
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseDataConnectionStateChanged(
-                @NonNull PreciseDataConnectionState dataConnectionState);
-    }
-
-    /**
-     * Interface for Single Radio Voice Call Continuity listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface SrvccStateChangedListener {
-        /**
-         * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
-         * (SRVCC) state for the currently active call on the registered subscription.
-         *
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onSrvccStateChanged(@SrvccState int srvccState);
-    }
-
-    /**
-     * Interface for SIM voice activation state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface VoiceActivationStateChangedListener {
-        /**
-         * Callback invoked when the SIM voice activation state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state is the current SIM voice activation state
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onVoiceActivationStateChanged(@SimActivationState int state);
-
-    }
-
-    /**
-     * Interface for SIM data activation state listener.
-     */
-    public interface DataActivationStateChangedListener {
-        /**
-         * Callback invoked when the SIM data activation state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state is the current SIM data activation state
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivationStateChanged(@SimActivationState int state);
-    }
-
-    /**
-     * Interface for user mobile data state listener.
-     */
-    public interface UserMobileDataStateChangedListener {
-        /**
-         * Callback invoked when the user mobile data state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param enabled indicates whether the current user mobile data state is enabled or
-         *                disabled.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onUserMobileDataStateChanged(boolean enabled);
-    }
-
-    /**
-     * Interface for display info listener.
-     */
-    public interface DisplayInfoChangedListener {
-        /**
-         * Callback invoked when the display info has changed on the registered subscription.
-         * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
-         * based on carrier policy.
-         *
-         * @param telephonyDisplayInfo The display information.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
-    }
-
-    /**
-     * Interface for the current emergency number list listener.
-     */
-    public interface EmergencyNumberListChangedListener {
-        /**
-         * Callback invoked when the current emergency number list has changed on the registered
-         * subscription.
-         *
-         * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
-         * was called.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * given subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param emergencyNumberList Map associating all active subscriptions on the device with
-         *                            the list of emergency numbers originating from that
-         *                            subscription.
-         *                            If there are no active subscriptions, the map will contain a
-         *                            single entry with
-         *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
-         *                            the key and a list of emergency numbers as the value. If no
-         *                            emergency number information is available, the value will be
-         *                            empty.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onEmergencyNumberListChanged(
-                @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
-    }
-
-    /**
-     * Interface for outgoing emergency call listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OutgoingEmergencyCallListener {
-        /**
-         * Callback invoked when an outgoing call is placed to an emergency number.
-         *
-         * This method will be called when an emergency call is placed on any subscription
-         * (including the no-SIM case), regardless of which subscription this listener was
-         * registered on.
-         *
-         * The default implementation of this method calls
-         * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes.
-         * Do not call {@code super(...)} from within your implementation unless you want
-         * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
-         *
-         * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
-         *                              placed to.
-         * @param subscriptionId The subscription ID used to place the emergency call. If the
-         *                       emergency call was placed without a valid subscription
-         *                       (e.g. when there are no SIM cards in the device), this will be
-         *                       equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
-         */
-        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
-                                     int subscriptionId);
-    }
-
-    /**
-     * Interface for outgoing emergency sms listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OutgoingEmergencySmsListener {
-        /**
-         * Smsback invoked when an outgoing sms is sent to an emergency number.
-         *
-         * This method will be called when an emergency sms is sent on any subscription,
-         * regardless of which subscription this listener was registered on.
-         *
-         * The default implementation of this method calls
-         * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
-         * not call {@code super(...)} from within your implementation unless you want
-         * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
-         *
-         * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
-         * @param subscriptionId The subscription ID used to send the emergency sms.
-         */
-        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
-                                    int subscriptionId);
-    }
-
-    /**
-     * Interface for phone capability listener.
-     *
-     */
-    public interface PhoneCapabilityChangedListener {
-        /**
-         * Callback invoked when phone capability changes.
-         * Note, this callback triggers regardless of registered subscription.
-         *
-         * @param capability the new phone capability
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
-    }
-
-    /**
-     * Interface for active data subscription ID listener.
-     */
-    public interface ActiveDataSubscriptionIdChangedListener {
-        /**
-         * Callback invoked when active data subscription ID changes.
-         * Note, this callback triggers regardless of registered subscription.
-         *
-         * @param subId current subscription used to setup Cellular Internet data.
-         *              For example, it could be the current active opportunistic subscription
-         *              in use, or the subscription user selected as default data subscription in
-         *              DSDS mode.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onActiveDataSubscriptionIdChanged(int subId);
-    }
-
-    /**
-     * Interface for modem radio power state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface RadioPowerStateChangedListener {
-        /**
-         * Callback invoked when modem radio power state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state the modem radio power state
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onRadioPowerStateChanged(@RadioPowerState int state);
-    }
-
-    /**
-     * Interface for carrier network listener.
-     */
-    public interface CarrierNetworkChangeListener {
-        /**
-         * Callback invoked when telephony has received notice from a carrier
-         * app that a network action that could result in connectivity loss
-         * has been requested by an app using
-         * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
-         *
-         * This is optional and is only used to allow the system to provide alternative UI while
-         * telephony is performing an action that may result in intentional, temporary network
-         * lack of connectivity.
-         *
-         * Note, this callback is pinned to the registered subscription and will be invoked when
-         * the notifying carrier app has carrier privilege rule on the registered
-         * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
-         *
-         * @param active If the carrier network change is or shortly will be active,
-         *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
-         */
-        public void onCarrierNetworkChange(boolean active);
-    }
-
-    /**
-     * Interface for registration failures listener.
-     */
-    public interface RegistrationFailedListener {
-        /**
-         * Report that Registration or a Location/Routing/Tracking Area update has failed.
-         *
-         * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
-         * area update fails. This includes procedures that do not necessarily result in a change of
-         * the modem's registration status. If the modem's registration status changes, that is
-         * reflected in the onNetworkStateChanged() and subsequent
-         * get{Voice/Data}RegistrationState().
-         *
-         * <p>Because registration failures are ephemeral, this callback is not sticky.
-         * Registrants will not receive the most recent past value when registering.
-         *
-         * @param cellIdentity the CellIdentity, which must include the globally unique identifier
-         *        for the cell (for example, all components of the CGI or ECGI).
-         * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
-         *         cell that was chosen for the failed registration attempt.
-         * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
-         * @param causeCode the primary failure cause code of the procedure.
-         *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
-         *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
-         *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
-         *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
-         *        Integer.MAX_VALUE if this value is unused.
-         * @param additionalCauseCode the cause code of any secondary/combined procedure
-         *                            if appropriate. For UMTS, if a combined attach succeeds for
-         *                            PS only, then the GMM cause code shall be included as an
-         *                            additionalCauseCode. For LTE (ESM), cause codes are in
-         *                            TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
-         */
-        @RequiresPermission(allOf = {
-                Manifest.permission.READ_PRECISE_PHONE_STATE,
-                Manifest.permission.ACCESS_FINE_LOCATION
-        })
-        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-                                         @NonNull String chosenPlmn, @Domain int domain,
-                                         int causeCode, int additionalCauseCode);
-    }
-
-    /**
-     * Interface for call attributes listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface CallAttributesChangedListener {
-        /**
-         * Callback invoked when the call attributes changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param callAttributes the call attributes
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
-    }
-
-    /**
-     * Interface for barring information listener.
-     */
-    public interface BarringInfoChangedListener {
-        /**
-         * Report updated barring information for the current camped/registered cell.
-         *
-         * <p>Barring info is provided for all services applicable to the current camped/registered
-         * cell, for the registered PLMN and current access class/access category.
-         *
-         * @param barringInfo for all services on the current cell.
-         * @see android.telephony.BarringInfo
-         */
-        @RequiresPermission(allOf = {
-                Manifest.permission.READ_PRECISE_PHONE_STATE,
-                Manifest.permission.ACCESS_FINE_LOCATION
-        })
-        public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
-    }
-
-    /**
-     * Interface for current physical channel configuration listener.
-     */
-    public interface PhysicalChannelConfigChangedListener {
-        /**
-         * Callback invoked when the current physical channel configuration has changed
-         *
-         * @param configs List of the current {@link PhysicalChannelConfig}s
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
-    }
-
-    /**
-     * Interface for data enabled listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface DataEnabledChangedListener {
-        /**
-         * Callback invoked when the data enabled changes.
-         *
-         * @param enabled {@code true} if data is enabled, otherwise disabled.
-         * @param reason Reason for data enabled/disabled.
-         *               See {@link TelephonyManager.DataEnabledReason}.
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onDataEnabledChanged(boolean enabled,
-                                  @DataEnabledReason int reason);
+        callback = new IPhoneStateListenerStub(this, e);
     }
 
     /**
@@ -1999,7 +707,6 @@
      * same as above, but with the network type.  Both called.
      */
     public void onDataConnectionStateChanged(int state, int networkType) {
-        // default implementation empty
     }
 
     /**
@@ -2047,7 +754,6 @@
      * @param cellInfo is the list of currently visible cells.
      */
     public void onCellInfoChanged(List<CellInfo> cellInfo) {
-        // default implementation empty
     }
 
     /**
@@ -2061,7 +767,7 @@
      * @param callState {@link PreciseCallState}
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
         // default implementation empty
@@ -2080,9 +786,9 @@
      * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
      *
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
-            @PreciseDisconnectCauses int preciseDisconnectCause) {
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+    public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+            int preciseDisconnectCause) {
         // default implementation empty
     }
 
@@ -2098,7 +804,7 @@
      * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
      *
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
         // default implementation empty
     }
@@ -2120,7 +826,7 @@
      *
      * @param dataConnectionState {@link PreciseDataConnectionState}
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
     public void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState) {
         // default implementation empty
@@ -2158,7 +864,6 @@
      */
     @SystemApi
     public void onSrvccStateChanged(@SrvccState int srvccState) {
-        // default implementation empty
 
     }
 
@@ -2177,7 +882,6 @@
      */
     @SystemApi
     public void onVoiceActivationStateChanged(@SimActivationState int state) {
-        // default implementation empty
     }
 
     /**
@@ -2194,7 +898,6 @@
      * @hide
      */
     public void onDataActivationStateChanged(@SimActivationState int state) {
-        // default implementation empty
     }
 
     /**
@@ -2222,7 +925,7 @@
      *
      * @param telephonyDisplayInfo The display information.
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE))
     public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         // default implementation empty
     }
@@ -2355,7 +1058,7 @@
      * @param capability the new phone capability
      * @hide
      */
-    public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) {
+    public void onPhoneCapabilityChanged(PhoneCapability capability) {
         // default implementation empty
     }
 
@@ -2399,8 +1102,7 @@
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
-     *
+     * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}
      * @param state the modem radio power state
      * @hide
      */
@@ -2476,6 +1178,18 @@
     }
 
     /**
+     * Callback invoked when the current physical channel configuration has changed
+     *
+     * @param configs List of the current {@link PhysicalChannelConfig}s
+     * @hide
+     */
+    @SystemApi
+    public void onPhysicalChannelConfigurationChanged(
+            @NonNull List<PhysicalChannelConfig> configs) {
+        // default implementation empty
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      *
@@ -2498,6 +1212,7 @@
         public void onServiceStateChanged(ServiceState serviceState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
         }
@@ -2505,6 +1220,7 @@
         public void onSignalStrengthChanged(int asu) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
         }
@@ -2512,6 +1228,7 @@
         public void onMessageWaitingIndicatorChanged(boolean mwi) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
         }
@@ -2519,6 +1236,7 @@
         public void onCallForwardingIndicatorChanged(boolean cfi) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
         }
@@ -2530,6 +1248,7 @@
                     cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation();
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
         }
@@ -2537,6 +1256,7 @@
         public void onCallStateChanged(int state, String incomingNumber) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
         }
@@ -2544,6 +1264,7 @@
         public void onDataConnectionStateChanged(int state, int networkType) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             if (state == TelephonyManager.DATA_DISCONNECTING
                     && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
                 Binder.withCleanCallingIdentity(() -> mExecutor.execute(
@@ -2564,6 +1285,7 @@
         public void onDataActivity(int direction) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
         }
@@ -2571,6 +1293,7 @@
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
         }
@@ -2578,6 +1301,7 @@
         public void onCellInfoChanged(List<CellInfo> cellInfo) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
         }
@@ -2585,6 +1309,7 @@
         public void onPreciseCallStateChanged(PreciseCallState callState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
         }
@@ -2592,6 +1317,7 @@
         public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
                             disconnectCause, preciseDisconnectCause)));
@@ -2601,6 +1327,7 @@
                 PreciseDataConnectionState dataConnectionState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
@@ -2618,6 +1345,7 @@
         public void onSrvccStateChanged(int state) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
         }
@@ -2625,6 +1353,7 @@
         public void onVoiceActivationStateChanged(int activationState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onVoiceActivationStateChanged(activationState)));
@@ -2633,6 +1362,7 @@
         public void onDataActivationStateChanged(int activationState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onDataActivationStateChanged(activationState)));
@@ -2641,6 +1371,7 @@
         public void onUserMobileDataStateChanged(boolean enabled) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onUserMobileDataStateChanged(enabled)));
@@ -2649,6 +1380,7 @@
         public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onDisplayInfoChanged(telephonyDisplayInfo)));
@@ -2657,6 +1389,7 @@
         public void onOemHookRawEvent(byte[] rawData) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
         }
@@ -2664,6 +1397,7 @@
         public void onCarrierNetworkChange(boolean active) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
         }
@@ -2671,6 +1405,7 @@
         public void onEmergencyNumberListChanged(Map emergencyNumberList) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
@@ -2680,6 +1415,7 @@
                 int subscriptionId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber,
@@ -2690,6 +1426,7 @@
                 int subscriptionId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onOutgoingEmergencySms(sentEmergencyNumber, subscriptionId)));
@@ -2698,6 +1435,7 @@
         public void onPhoneCapabilityChanged(PhoneCapability capability) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
         }
@@ -2705,6 +1443,7 @@
         public void onRadioPowerStateChanged(@RadioPowerState int state) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
         }
@@ -2712,6 +1451,7 @@
         public void onCallAttributesChanged(CallAttributes callAttributes) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
         }
@@ -2719,6 +1459,7 @@
         public void onActiveDataSubIdChanged(int subId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onActiveDataSubscriptionIdChanged(subId)));
         }
@@ -2726,51 +1467,44 @@
         public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
+
         }
 
         public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-                @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+                @NonNull String chosenPlmn, int domain,
+                int causeCode, int additionalCauseCode) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onRegistrationFailed(
-                                cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
             // default implementation empty
         }
 
         public void onBarringInfoChanged(BarringInfo barringInfo) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo)));
         }
 
-        public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+        public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
-            PhysicalChannelConfigChangedListener listener =
-                    (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get();
-            if (listener == null) return;
+            if (psl == null) return;
+
             Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
-                            configs)));
-        }
-
-        public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) {
-            if ((mPhoneStateListenerWeakRef.get() instanceof DataEnabledChangedListener)) {
-                DataEnabledChangedListener listener =
-                        (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get();
-                if (listener == null) return;
-
-                Binder.withCleanCallingIdentity(
-                        () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
-                                enabled, reason)));
-            }
+                    () -> mExecutor.execute(
+                            () -> psl.onPhysicalChannelConfigurationChanged(configs)));
         }
     }
 
+
     private void log(String s) {
         Rlog.d(LOG_TAG, s);
     }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index c706e21..24ed29a 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -15,7 +15,6 @@
  */
 package android.telephony;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -25,9 +24,6 @@
 import android.content.Context;
 import android.os.Binder;
 import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Annotation.CallState;
@@ -41,7 +37,6 @@
 import android.telephony.Annotation.SrvccState;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
-import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -50,7 +45,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -212,7 +206,7 @@
     }
 
     /**
-     * To check the SDK version for {@link #listenWithEventList}.
+     * To check the SDK version for {@link #listenForSubscriber}.
      */
     @ChangeId
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
@@ -224,23 +218,23 @@
      * @param pkg Package name
      * @param featureId Feature ID
      * @param listener Listener providing callback
-     * @param events List events
+     * @param events Events
      * @param notifyNow Whether to notify instantly
      */
-    public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId,
-            @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) {
+    public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
+            @NonNull PhoneStateListener listener, long events, boolean notifyNow) {
         try {
             // subId from PhoneStateListener is deprecated Q on forward, use the subId from
             // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
             if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
                 // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
                 // the only place to set mSubId and its for "informational" only.
-                listener.mSubId = (events.length == 0)
+                listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
                         ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
             } else if (listener.mSubId != null) {
                 subId = listener.mSubId;
             }
-            sRegistry.listenWithEventList(
+            sRegistry.listenForSubscriber(
                     subId, pkg, featureId, listener.callback, events, notifyNow);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -777,349 +771,13 @@
      * @param subId the subId
      * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
      */
-    public void notifyPhysicalChannelConfigForSubscriber(
+    public void notifyPhysicalChannelConfigurationForSubscriber(
             int subId, List<PhysicalChannelConfig> configs) {
         try {
-            sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs);
+            sRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs);
         } catch (RemoteException ex) {
             // system server crash
         }
     }
 
-    public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) {
-
-        Set<Integer> eventList = new ArraySet<>();
-
-        if (listener instanceof PhoneStateListener.ServiceStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CellLocationChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CallStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.DataActivityListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthsChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CellInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.SrvccStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
-        }
-
-        if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
-        }
-
-        if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) {
-            eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.RegistrationFailedListener) {
-            eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
-        }
-
-        if (listener instanceof PhoneStateListener.CallAttributesChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.BarringInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-        }
-
-        if (listener instanceof PhoneStateListener.DataEnabledChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
-        }
-
-        return eventList;
-    }
-
-    private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) {
-
-        Set<Integer> eventList = new ArraySet<>();
-
-        if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
-            eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
-            eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
-            eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
-            eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
-        }
-
-        if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
-        }
-        return eventList;
-
-    }
-
-    /**
-     * Registers a listener object to receive notification of changes
-     * in specified telephony states.
-     * <p>
-     * To register a listener, pass a {@link PhoneStateListener} which implements
-     * interfaces of events. For example,
-     * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
-     * {@link PhoneStateListener.ServiceStateChangedListener}.
-     *
-     * At registration, and when a specified telephony state changes, the telephony manager invokes
-     * the appropriate callback method on the listener object and passes the current (updated)
-     * values.
-     * <p>
-     *
-     * If this TelephonyManager object has been created with
-     * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId.
-     * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
-     * To listen events for multiple subIds, pass a separate listener object to
-     * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}.
-     *
-     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
-     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
-     * {@link SecurityException} will be thrown otherwise.
-     *
-     * This API should be used sparingly -- large numbers of listeners will cause system
-     * instability. If a process has registered too many listeners without unregistering them, it
-     * may encounter an {@link IllegalStateException} when trying to register more listeners.
-     *
-     * @param listener The {@link PhoneStateListener} object to register.
-     */
-    public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId,
-            String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
-            boolean notifyNow) {
-        registerPhoneStateListener(executor, subId, pkgName, attributionTag, listener,
-                getEventsFromListener(listener), notifyNow);
-    }
-
-    public void registerPhoneStateListenerWithEvents(int subId, String pkgName,
-            String attributionTag, @NonNull PhoneStateListener listener, int events,
-            boolean notifyNow) {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
-        registerPhoneStateListener(new HandlerExecutor(new Handler(Looper.myLooper())),
-                subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow);
-    }
-
-    private void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId,
-            String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
-            @NonNull Set<Integer> events, boolean notifyNow) {
-        if (listener == null) {
-            throw new IllegalStateException("telephony service is null.");
-        }
-
-        listener.setExecutor(executor);
-        listenWithEventList(subId, pkgName, attributionTag, listener,
-                events.stream().mapToInt(i -> i).toArray(), notifyNow);
-    }
-
-    /**
-     * Unregister an existing {@link PhoneStateListener}.
-     *
-     * @param listener The {@link PhoneStateListener} object to unregister.
-     */
-    public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag,
-                                             @NonNull PhoneStateListener listener,
-                                             boolean notifyNow) {
-        listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow);
-    }
 }
diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl
new file mode 100644
index 0000000..57666ff
--- /dev/null
+++ b/core/java/android/uwb/AngleOfArrivalSupport.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum AngleOfArrivalSupport {
+  /**
+   * The device does not support angle of arrival
+   */
+  NONE,
+
+  /**
+   * The device supports planar angle of arrival
+   */
+  TWO_DIMENSIONAL,
+
+  /**
+   * The device does supports three dimensional angle of arrival with hemispherical azimuth angles
+   */
+  THREE_DIMENSIONAL_HEMISPHERICAL,
+
+  /**
+   * The device does supports three dimensional angle of arrival with full azimuth angles
+   */
+  THREE_DIMENSIONAL_SPHERICAL,
+}
+
diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/CloseReason.aidl
new file mode 100644
index 0000000..bef129e
--- /dev/null
+++ b/core/java/android/uwb/CloseReason.aidl
@@ -0,0 +1,58 @@
+/*
+ * 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.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum CloseReason {
+  /**
+   * Unknown reason
+   */
+  UNKNOWN,
+
+  /**
+   * A local API call triggered the close, such as a call to
+   * IUwbAdapter.stopRanging.
+   */
+  LOCAL_API,
+
+  /**
+   * The maximum number of sessions has been reached. This error may be generated
+   * for an active session if a higher priority session begins.
+   */
+  MAX_SESSIONS_REACHED,
+
+  /**
+   * The system state has changed resulting in the session ending (e.g. the user
+   * disables UWB, or the user's locale changes and an active channel is no longer
+   * permitted to be used).
+   */
+  SYSTEM_POLICY,
+
+  /**
+   * The remote device has requested to terminate the session
+   */
+  REMOTE_REQUEST,
+
+  /**
+   * The session was closed for a protocol specific reason
+   */
+  PROTOCOL_SPECIFIC,
+}
+
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
new file mode 100644
index 0000000..d29ed34
--- /dev/null
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -0,0 +1,170 @@
+/*
+ * 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.uwb;
+
+import android.os.PersistableBundle;
+import android.uwb.AngleOfArrivalSupport;
+import android.uwb.IUwbAdapterStateCallbacks;
+import android.uwb.IUwbRangingCallbacks;
+import android.uwb.SessionHandle;
+
+/**
+ * @hide
+ */
+interface IUwbAdapter {
+  /*
+   * Register the callbacks used to notify the framework of events and data
+   *
+   * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
+   * function must be called immediately following registration with the current
+   * state of the UWB adapter.
+   *
+   * @param callbacks callback to provide range and status updates to the framework
+   */
+  void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks);
+
+  /*
+   * Unregister the callbacks used to notify the framework of events and data
+   *
+   * Calling this function with an unregistered callback is a no-op
+   *
+   * @param callbacks callback to unregister
+   */
+  void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
+
+  /**
+   * Returns true if ranging is supported, false otherwise
+   */
+  boolean isRangingSupported();
+
+  /**
+   * Get the angle of arrival supported by this device
+   *
+   * @return the angle of arrival type supported
+   */
+  AngleOfArrivalSupport getAngleOfArrivalSupport();
+
+  /**
+   * Generates a list of the supported 802.15.4z channels
+   *
+   * The list must be prioritized in the order of preferred channel usage.
+   *
+   * The list must only contain channels that are permitted to be used in the
+   * device's current location.
+   *
+   * @return an array of support channels on the device for the current location.
+   */
+  int[] getSupportedChannels();
+
+  /**
+   * Generates a list of the supported 802.15.4z preamble codes
+   *
+   * The list must be prioritized in the order of preferred preamble usage.
+   *
+   * The list must only contain preambles that are permitted to be used in the
+   * device's current location.
+   *
+   * @return an array of supported preambles on the device for the current
+   *         location.
+   */
+  int[] getSupportedPreambleCodes();
+
+  /**
+   * Get the accuracy of the ranging timestamps
+   *
+   * @return accuracy of the ranging timestamps in nanoseconds
+   */
+  long getTimestampResolutionNanos();
+
+  /**
+   * Get the supported number of simultaneous ranging sessions
+   *
+   * @return the supported number of simultaneous ranging sessions
+   */
+  int getMaxSimultaneousSessions();
+
+  /**
+   * Get the maximum number of remote devices per session
+   *
+   * @return the maximum number of remote devices supported in a single session
+   */
+  int getMaxRemoteDevicesPerSession();
+
+  /**
+   * Provides the capabilities and features of the device
+   *
+   * @return specification specific capabilities and features of the device
+   */
+  PersistableBundle getSpecificationInfo();
+
+  /**
+   * Request to start a new ranging session
+   *
+   * This function must return before calling IUwbAdapterCallbacks
+   * #onRangingStarted, #onRangingClosed, or #onRangingResult.
+   *
+   * A ranging session does not need to be started before returning.
+   *
+   * IUwbAdapterCallbacks#onRangingStarted must be called within
+   * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
+   * if the ranging session is scheduled to start successfully.
+   *
+   * IUwbAdapterCallbacks#onRangingStartFailed must be called within
+   * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
+   * if the ranging session fails to be scheduled to start successfully.
+   *
+   * @param rangingCallbacks the callbacks used to deliver ranging information
+   * @param parameters the configuration to use for ranging
+   * @return a SessionHandle used to identify this ranging request
+   */
+  SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks,
+                             in PersistableBundle parameters);
+
+  /**
+   * Stop and close ranging for the session associated with the given handle
+   *
+   * Calling with an invalid handle or a handle that has already been closed
+   * is a no-op.
+   *
+   * IUwbAdapterCallbacks#onRangingClosed must be called within
+   * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called.
+   *
+   * @param sessionHandle the session handle to stop ranging for
+   */
+  void closeRanging(in SessionHandle sessionHandle);
+
+  /**
+   * The maximum allowed time to start a ranging session.
+   */
+  const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD
+
+  /**
+   * The maximum allowed time to notify the framework that a session has been
+   * closed.
+   */
+  const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
+
+  /**
+   * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY
+   */
+  const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416;
+
+  /**
+   * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY
+   */
+  const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1;
+}
diff --git a/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl
new file mode 100644
index 0000000..d928eab
--- /dev/null
+++ b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.uwb;
+
+import android.uwb.StateChangeReason;
+
+/**
+ * @hide
+ */
+interface IUwbAdapterStateCallbacks {
+  /**
+   * Called whenever the adapter state changes
+   *
+   * @param isEnabled true if the adapter is enabled, false otherwise
+   * @param reason the reason that the state has changed
+   */
+  void onAdapterStateChanged(boolean isEnabled, StateChangeReason reason);
+}
\ No newline at end of file
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
new file mode 100644
index 0000000..1fc3bfd
--- /dev/null
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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.uwb;
+
+import android.os.PersistableBundle;
+import android.uwb.CloseReason;
+import android.uwb.RangingReport;
+import android.uwb.SessionHandle;
+import android.uwb.StartFailureReason;
+
+/**
+ * @hide
+ */
+interface IUwbRangingCallbacks {
+  /**
+   * Called when ranging has started
+   *
+   * May output parameters generated by the lower layers that must be sent to the
+   * remote device(s). The PersistableBundle must be constructed using the UWB
+   * support library.
+   *
+   * @param sessionHandle the session the callback is being invoked for
+   * @param rangingOutputParameters parameters generated by the lower layer that
+   *                                should be sent to the remote device.
+   */
+  void onRangingStarted(in SessionHandle sessionHandle,
+                        in PersistableBundle parameters);
+
+  /**
+   * Called when a ranging session fails to start
+   *
+   * @param sessionHandle the session the callback is being invoked for
+   * @param reason the reason the session failed to start
+   * @param parameters protocol specific parameters
+   */
+  void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason,
+                            in PersistableBundle parameters);
+  /**
+   * Called when a ranging session is closed
+   *
+   * @param sessionHandle the session the callback is being invoked for
+   * @param reason the reason the session was closed
+   * @param parameters protocol specific parameters
+   */
+  void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason,
+                       in PersistableBundle parameters);
+
+  /**
+   * Provides a new RangingResult to the framework
+   *
+   * The reported timestamp for a ranging measurement must be calculated as the
+   * time which the ranging round that generated this measurement concluded.
+   *
+   * @param sessionHandle an identifier to associate the ranging results with a
+   *                      session that is active
+   * @param result the ranging report
+   */
+  void onRangingResult(in SessionHandle sessionHandle, in RangingReport result);
+}
diff --git a/core/java/android/uwb/MeasurementStatus.aidl b/core/java/android/uwb/MeasurementStatus.aidl
new file mode 100644
index 0000000..5fa1554
--- /dev/null
+++ b/core/java/android/uwb/MeasurementStatus.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum MeasurementStatus {
+  /**
+   * Ranging was successful
+   */
+  SUCCESS,
+
+  /**
+   * The remote device is out of range
+   */
+  FAILURE_OUT_OF_RANGE,
+
+  /**
+   * An unknown failure has occurred.
+   */
+   FAILURE_UNKNOWN,
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/core/java/android/uwb/RangingReport.aidl
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
copy to core/java/android/uwb/RangingReport.aidl
index d7a3af0..c32747a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
+++ b/core/java/android/uwb/RangingReport.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,13 +11,9 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.classifier;
+package android.uwb;
 
-public class PointerCountEvaluator {
-    public static float evaluate(int value) {
-        return (value - 1) * (value - 1);
-    }
-}
+parcelable RangingReport;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/core/java/android/uwb/SessionHandle.aidl
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
copy to core/java/android/uwb/SessionHandle.aidl
index d7a3af0..58a7dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
+++ b/core/java/android/uwb/SessionHandle.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,13 +11,9 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.classifier;
+package android.uwb;
 
-public class PointerCountEvaluator {
-    public static float evaluate(int value) {
-        return (value - 1) * (value - 1);
-    }
-}
+parcelable SessionHandle;
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
new file mode 100644
index 0000000..928fcbdc
--- /dev/null
+++ b/core/java/android/uwb/SessionHandle.java
@@ -0,0 +1,79 @@
+/*
+ * 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.uwb;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public final class SessionHandle implements Parcelable  {
+    private final int mId;
+
+    public SessionHandle(int id) {
+        mId = id;
+    }
+
+    protected SessionHandle(Parcel in) {
+        mId = in.readInt();
+    }
+
+    public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() {
+        @Override
+        public SessionHandle createFromParcel(Parcel in) {
+            return new SessionHandle(in);
+        }
+
+        @Override
+        public SessionHandle[] newArray(int size) {
+            return new SessionHandle[size];
+        }
+    };
+
+    public int getId() {
+        return mId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof SessionHandle) {
+            SessionHandle other = (SessionHandle) obj;
+            return mId == other.mId;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "SessionHandle [id=" + mId + "]";
+    }
+}
diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl
new file mode 100644
index 0000000..4d9c962
--- /dev/null
+++ b/core/java/android/uwb/StartFailureReason.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum StartFailureReason {
+  /**
+   * Unknown start failure reason
+   */
+  UNKNOWN,
+
+  /**
+   * The provided parameters were invalid and ranging could not start
+   */
+  BAD_PARAMETERS,
+
+  /**
+   * The maximum number of sessions has been reached. This error may be generated
+   * for an active session if a higher priority session begins.
+   */
+  MAX_SESSIONS_REACHED,
+
+  /**
+   * The system state has changed resulting in the session ending (e.g. the user
+   * disables UWB, or the user's locale changes and an active channel is no longer
+   * permitted to be used).
+   */
+  SYSTEM_POLICY,
+
+  /**
+   * The session could not start because of a protocol specific reason.
+   */
+  PROTOCOL_SPECIFIC,
+}
+
diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl
new file mode 100644
index 0000000..46a6e2e
--- /dev/null
+++ b/core/java/android/uwb/StateChangeReason.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum StateChangeReason {
+  /**
+   * The state changed for an unknown reason
+   */
+  UNKNOWN,
+
+  /**
+   * The adapter state changed because a session started.
+   */
+  SESSION_STARTED,
+
+
+  /**
+   * The adapter state changed because all sessions were closed.
+   */
+  ALL_SESSIONS_CLOSED,
+
+  /**
+   * The adapter state changed because of a device system change.
+   */
+  SYSTEM_POLICY,
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/core/java/android/uwb/UwbAddress.aidl
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
copy to core/java/android/uwb/UwbAddress.aidl
index d7a3af0..a202b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
+++ b/core/java/android/uwb/UwbAddress.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,13 +11,9 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.classifier;
+package android.uwb;
 
-public class PointerCountEvaluator {
-    public static float evaluate(int value) {
-        return (value - 1) * (value - 1);
-    }
-}
+parcelable UwbAddress;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 924fc6d..0533533 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -668,22 +668,24 @@
     void setShouldShowIme(int displayId, boolean shouldShow);
 
     /**
-     * Waits for transactions to get applied before injecting input.
-     * This includes waiting for the input windows to get sent to InputManager.
+     * Waits for transactions to get applied before injecting input, optionally waiting for
+     * animations to complete. This includes waiting for the input windows to get sent to
+     * InputManager.
      *
      * This is needed for testing since the system add windows and injects input
      * quick enough that the windows don't have time to get sent to InputManager.
      */
-    boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode);
+    boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode,
+            boolean waitForAnimations);
 
     /**
-     * Waits until all animations have completed and input information has been sent from
-     * WindowManager to native InputManager.
+     * Waits until input information has been sent from WindowManager to native InputManager,
+     * optionally waiting for animations to complete.
      *
      * This is needed for testing since we need to ensure input information has been propagated to
      * native InputManager before proceeding with tests.
      */
-    void syncInputTransactions();
+    void syncInputTransactions(boolean waitForAnimations);
 
     /**
      * Returns whether SurfaceFlinger layer tracing is enabled.
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 038dff6..7d1adc36 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -144,6 +144,20 @@
     }
 
     /**
+     * Called when a Pointer Capture event is received.
+     *
+     * @param pointerCaptureEnabled if true, the window associated with this input channel has just
+     *                              received Pointer Capture
+     *                              if false, the window associated with this input channel has just
+     *                              lost Pointer Capture
+     * @see View#requestPointerCapture()
+     * @see View#releasePointerCapture()
+     */
+    // Called from native code.
+    public void onPointerCaptureEvent(boolean pointerCaptureEnabled) {
+    }
+
+    /**
      * Called when a batched input event is pending.
      *
      * The batched input event will continue to accumulate additional movement
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index f7fbb1c..673ed0d 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -43,12 +43,13 @@
  */
 @RemoteViews.RemoteView
 public class NotificationHeaderView extends FrameLayout {
-    private final int mContentEndMargin;
     private final int mHeadingEndMargin;
+    private final int mTouchableHeight;
     private OnClickListener mExpandClickListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private NotificationTopLineView mTopLineView;
     private NotificationExpandButton mExpandButton;
+    private View mAltExpandTarget;
     private CachingIconView mIcon;
     private Drawable mBackground;
     private boolean mEntireHeaderClickable;
@@ -82,8 +83,8 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         Resources res = getResources();
-        mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
         mHeadingEndMargin = res.getDimensionPixelSize(R.dimen.notification_heading_margin_end);
+        mTouchableHeight = res.getDimensionPixelSize(R.dimen.notification_header_touchable_height);
         mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
     }
 
@@ -93,6 +94,7 @@
         mIcon = findViewById(R.id.icon);
         mTopLineView = findViewById(R.id.notification_top_line);
         mExpandButton = findViewById(R.id.expand_button);
+        mAltExpandTarget = findViewById(R.id.alternate_expand_target);
         setClipToPadding(false);
     }
 
@@ -146,6 +148,7 @@
     public void setOnClickListener(@Nullable OnClickListener l) {
         mExpandClickListener = l;
         mExpandButton.setOnClickListener(mExpandClickListener);
+        mAltExpandTarget.setOnClickListener(mExpandClickListener);
         updateTouchListener();
     }
 
@@ -187,6 +190,7 @@
 
         private final ArrayList<Rect> mTouchRects = new ArrayList<>();
         private Rect mExpandButtonRect;
+        private Rect mAltExpandTargetRect;
         private int mTouchSlop;
         private boolean mTrackGesture;
         private float mDownX;
@@ -199,6 +203,7 @@
             mTouchRects.clear();
             addRectAroundView(mIcon);
             mExpandButtonRect = addRectAroundView(mExpandButton);
+            mAltExpandTargetRect = addRectAroundView(mAltExpandTarget);
             addWidthRect();
             mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         }
@@ -206,7 +211,7 @@
         private void addWidthRect() {
             Rect r = new Rect();
             r.top = 0;
-            r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
+            r.bottom = mTouchableHeight;
             r.left = 0;
             r.right = getWidth();
             mTouchRects.add(r);
@@ -277,7 +282,8 @@
                 return true;
             }
             if (mExpandOnlyOnButton) {
-                return mExpandButtonRect.contains((int) x, (int) y);
+                return mExpandButtonRect.contains((int) x, (int) y)
+                        || mAltExpandTargetRect.contains((int) x, (int) y);
             }
             for (int i = 0; i < mTouchRects.size(); i++) {
                 Rect r = mTouchRects.get(i);
diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java
index a8eabe5..05636de 100644
--- a/core/java/android/view/NotificationTopLineView.java
+++ b/core/java/android/view/NotificationTopLineView.java
@@ -97,10 +97,8 @@
         final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
         final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
         final boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
-        int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
-                MeasureSpec.AT_MOST);
-        int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
-                MeasureSpec.AT_MOST);
+        int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth, MeasureSpec.AT_MOST);
+        int heightSpec = MeasureSpec.makeMeasureSpec(givenHeight, MeasureSpec.AT_MOST);
         int totalWidth = getPaddingStart();
         int maxChildHeight = -1;
         mMaxAscent = -1;
@@ -114,7 +112,7 @@
             final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
             int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
                     lp.leftMargin + lp.rightMargin, lp.width);
-            int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
+            int childHeightSpec = getChildMeasureSpec(heightSpec,
                     lp.topMargin + lp.bottomMargin, lp.height);
             child.measure(childWidthSpec, childHeightSpec);
             totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -131,37 +129,37 @@
         int endMargin = Math.max(mHeaderTextMarginEnd, getPaddingEnd());
         if (totalWidth > givenWidth - endMargin) {
             int overFlow = totalWidth - givenWidth + endMargin;
-            if (mAppName != null) {
-                // We are overflowing, lets shrink the app name first
-                overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
-                        mChildMinWidth);
-            }
 
-            if (mTitle != null) {
-                // still overflowing, we shrink the title text
-                overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mTitle,
-                        mChildMinWidth);
-            }
+            // First shrink the app name, down to a minimum size
+            overFlow = shrinkViewForOverflow(heightSpec, overFlow, mAppName, mChildMinWidth);
 
-            // still overflowing, we shrink the header text
-            overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
+            // Next, shrink the header text (this usually has subText)
+            //   This shrinks the subtext first, but not all the way (yet!)
+            overFlow = shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, mChildMinWidth);
 
-            // still overflowing, finally we shrink the secondary header text
-            shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
-                    0);
+            // Next, shrink the secondary header text  (this rarely has conversationTitle)
+            overFlow = shrinkViewForOverflow(heightSpec, overFlow, mSecondaryHeaderText, 0);
+
+            // Next, shrink the title text (this has contentTitle; only in headerless views)
+            overFlow = shrinkViewForOverflow(heightSpec, overFlow, mTitle, mChildMinWidth);
+
+            // Finally, if there is still overflow, shrink the header down to 0 if still necessary.
+            shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, 0);
         }
         setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight);
     }
 
     private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
             int minimumWidth) {
-        final int oldWidth = targetView.getMeasuredWidth();
-        if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
-            // we're still too big
-            int newSize = Math.max(minimumWidth, oldWidth - overFlow);
-            int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
-            targetView.measure(childWidthSpec, heightSpec);
-            overFlow -= oldWidth - newSize;
+        if (targetView != null) {
+            final int oldWidth = targetView.getMeasuredWidth();
+            if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
+                // we're still too big
+                int newSize = Math.max(minimumWidth, oldWidth - overFlow);
+                int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+                targetView.measure(childWidthSpec, heightSpec);
+                overFlow -= oldWidth - newSize;
+            }
         }
         return overFlow;
     }
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
index 4955289..db9c538 100644
--- a/core/java/android/view/OnReceiveContentListener.java
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -48,7 +48,7 @@
  *     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
  *
  *     &#64;Override
- *     public Payload onReceiveContent(TextView view, Payload payload) {
+ *     public Payload onReceiveContent(View view, Payload payload) {
  *         Map&lt;Boolean, Payload&gt; split = payload.partition(item -&gt; item.getUri() != null);
  *         if (split.get(true) != null) {
  *             ClipData clip = payload.getClip();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a88ad9f..30ec2b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9008,7 +9008,7 @@
     }
 
     /**
-     * Sets the listener to be {@link #onReceiveContent used} to handle insertion of
+     * Sets the listener to be {@link #performReceiveContent used} to handle insertion of
      * content into this view.
      *
      * <p>Depending on the type of view, this listener may be invoked for different scenarios. For
@@ -9039,7 +9039,6 @@
      *                  not be null or empty if a non-null listener is passed in.
      * @param listener The listener to use. This can be null to reset to the default behavior.
      */
-    @SuppressWarnings("rawtypes")
     public void setOnReceiveContentListener(@Nullable String[] mimeTypes,
             @Nullable OnReceiveContentListener listener) {
         if (listener != null) {
@@ -9055,27 +9054,46 @@
     }
 
     /**
-     * Receives the given content. Invokes the listener configured via
-     * {@link #setOnReceiveContentListener}; if no listener is set, the default implementation is a
-     * no-op (returns the passed-in content without acting on it).
+     * Receives the given content. If no listener is set, invokes {@link #onReceiveContent}. If a
+     * listener is {@link #setOnReceiveContentListener set}, invokes the listener instead; if the
+     * listener returns a non-null result, invokes {@link #onReceiveContent} to handle it.
      *
      * @param payload The content to insert and related metadata.
      *
      * @return The portion of the passed-in content that was not accepted (may be all, some, or none
      * of the passed-in content).
      */
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+    public @Nullable Payload performReceiveContent(@NonNull Payload payload) {
         final OnReceiveContentListener listener = (mListenerInfo == null) ? null
                 : getListenerInfo().mOnReceiveContentListener;
         if (listener != null) {
-            return listener.onReceiveContent(this, payload);
+            final Payload remaining = listener.onReceiveContent(this, payload);
+            return (remaining == null) ? null : onReceiveContent(remaining);
         }
+        return onReceiveContent(payload);
+    }
+
+    /**
+     * Implements the default behavior for receiving content for this type of view. The default
+     * view implementation is a no-op (returns the passed-in content without acting on it).
+     *
+     * <p>Widgets should override this method to define their default behavior for receiving
+     * content. Apps should {@link #setOnReceiveContentListener set a listener} to provide
+     * app-specific handling for receiving content.
+     *
+     * <p>See {@link #setOnReceiveContentListener} and {@link #performReceiveContent} for more info.
+     *
+     * @param payload The content to insert and related metadata.
+     *
+     * @return The portion of the passed-in content that was not handled (may be all, some, or none
+     * of the passed-in content).
+     */
+    public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
         return payload;
     }
 
     /**
-     * Returns the MIME types accepted by {@link #onReceiveContent} for this view, as
+     * Returns the MIME types accepted by {@link #performReceiveContent} for this view, as
      * configured via {@link #setOnReceiveContentListener}. By default returns null.
      *
      * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft
@@ -9092,7 +9110,7 @@
      * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
      * lowercase.
      *
-     * @return The MIME types accepted by {@link #onReceiveContent} for this view (may
+     * @return The MIME types accepted by {@link #performReceiveContent} for this view (may
      * include patterns such as "image/*").
      */
     public @Nullable String[] getOnReceiveContentMimeTypes() {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 72f76d1..436acef 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -410,6 +410,13 @@
     int TRANSIT_FLAG_APP_CRASHED = 0x10;
 
     /**
+     * Transition flag: A window in a new task is being opened behind an existing one in another
+     * activity's task.
+     * @hide
+     */
+    int TRANSIT_FLAG_OPEN_BEHIND = 0x20;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -417,7 +424,8 @@
             TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
             TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
             TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
-            TRANSIT_FLAG_APP_CRASHED
+            TRANSIT_FLAG_APP_CRASHED,
+            TRANSIT_FLAG_OPEN_BEHIND
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5e5d14f..0ed7ca7 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -207,12 +207,19 @@
     }
 
     /** @hide */
+    @Nullable
     protected SurfaceControl getSurfaceControl(View rootView) {
         final ViewRootImpl root = rootView.getViewRootImpl();
         if (root == null) {
             return null;
         }
-        final State s = mStateForWindow.get(root.mWindow.asBinder());
+        return getSurfaceControl(root.mWindow);
+    }
+
+    /** @hide */
+    @Nullable
+    protected SurfaceControl getSurfaceControl(IWindow window) {
+        final State s = mStateForWindow.get(window.asBinder());
         if (s == null) {
             return null;
         }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 299c41b..8fdcac7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2372,7 +2372,7 @@
                 return;
             }
             Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
-            Payload result = view.onReceiveContent(payload);
+            Payload result = view.performReceiveContent(payload);
             if (result != null) {
                 Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id
                         + ", view=" + view + ", clip=" + clip);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a92d1f5..1ab9edf 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -43,6 +43,8 @@
 import android.view.OnReceiveContentListener;
 import android.view.View;
 
+import com.android.internal.util.Preconditions;
+
 class ComposingText implements NoCopySpan {
 }
 
@@ -504,7 +506,7 @@
      */
     @Nullable
     public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0) return null;
+        Preconditions.checkArgumentNonnegative(length);
 
         final Editable content = getEditable();
         if (content == null) return null;
@@ -563,7 +565,7 @@
      */
     @Nullable
     public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0) return null;
+        Preconditions.checkArgumentNonnegative(length);
 
         final Editable content = getEditable();
         if (content == null) return null;
@@ -600,7 +602,8 @@
     @Nullable
     public SurroundingText getSurroundingText(
             @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength, int flags) {
-        if (beforeLength < 0 || afterLength < 0) return null;
+        Preconditions.checkArgumentNonnegative(beforeLength);
+        Preconditions.checkArgumentNonnegative(afterLength);
 
         final Editable content = getEditable();
         if (content == null) return null;
@@ -927,9 +930,9 @@
     }
 
     /**
-     * Default implementation which invokes {@link View#onReceiveContent} on the target view if the
-     * view {@link View#getOnReceiveContentMimeTypes allows} content insertion; otherwise returns
-     * false without any side effects.
+     * Default implementation which invokes {@link View#performReceiveContent} on the target
+     * view if the view {@link View#getOnReceiveContentMimeTypes allows} content insertion;
+     * otherwise returns false without any side effects.
      */
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
         ClipDescription description = inputContentInfo.getDescription();
@@ -954,6 +957,6 @@
                 .setLinkUri(inputContentInfo.getLinkUri())
                 .setExtras(opts)
                 .build();
-        return mTargetView.onReceiveContent(payload) == null;
+        return mTargetView.performReceiveContent(payload) == null;
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index da14f2c..00ba326 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2872,7 +2872,7 @@
             final OnReceiveContentListener.Payload payload =
                     new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
                     .build();
-            mTextView.onReceiveContent(payload);
+            mTextView.performReceiveContent(payload);
             if (dragDropIntoItself) {
                 deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength);
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fb13807..98f8087 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2152,7 +2152,7 @@
                     if (isTextEditable()) {
                         ClipData clip = ClipData.newPlainText("", result);
                         Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
-                        onReceiveContent(payload);
+                        performReceiveContent(payload);
                         if (mEditor != null) {
                             mEditor.refreshTextActionMode();
                         }
@@ -11858,7 +11858,7 @@
             return;
         }
         final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
-        onReceiveContent(payload);
+        performReceiveContent(payload);
     }
 
     @Override
@@ -12927,7 +12927,7 @@
         final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD)
                 .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
                 .build();
-        onReceiveContent(payload);
+        performReceiveContent(payload);
         sLastCutCopyOrTextChangedTime = 0;
     }
 
@@ -13728,17 +13728,12 @@
     }
 
     /**
-     * Receives the given content. Clients wishing to provide custom behavior should configure a
-     * listener via {@link #setOnReceiveContentListener}.
+     * Default {@link TextView} implementation for receiving content. Apps wishing to provide
+     * custom behavior should configure a listener via {@link #setOnReceiveContentListener}.
      *
-     * <p>If a listener is set, invokes the listener. If the listener returns a non-null result,
-     * executes the default platform handling for the portion of the content returned by the
-     * listener.
-     *
-     * <p>If no listener is set, executes the default platform behavior. For non-editable TextViews
-     * the default behavior is a no-op (returns the passed-in content without acting on it). For
-     * editable TextViews the default behavior coerces all content to text and inserts into the
-     * view.
+     * <p>For non-editable TextViews the default behavior is a no-op (returns the passed-in
+     * content without acting on it). For editable TextViews the default behavior coerces all
+     * content to text and inserts into the view.
      *
      * @param payload The content to insert and related metadata.
      *
@@ -13747,11 +13742,10 @@
      */
     @Override
     public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
-        Payload remaining = super.onReceiveContent(payload);
-        if (remaining != null && mEditor != null) {
-            return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, remaining);
+        if (mEditor != null) {
+            return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload);
         }
-        return remaining;
+        return payload;
     }
 
     private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e6a1661..6d98a59 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -52,7 +52,7 @@
     @UnsupportedAppUsage
     byte[] getStatistics();
 
-    ParcelFileDescriptor getStatisticsStream();
+    ParcelFileDescriptor getStatisticsStream(boolean updateAll);
 
     // Return true if we see the battery as currently charging.
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index c0d46f6..571ac1b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -37,34 +37,33 @@
     //TODO (163431584): need also consider other refresh rates.
     private static final long JANK_THRESHOLD_NANOS = 1000000000 / 60;
     private static final long UNKNOWN_TIMESTAMP = -1;
+    public static final int NANOS_IN_MILLISECOND = 1_000_000;
 
     private final HardwareRendererObserver mObserver;
+    private final int mTraceThresholdMissedFrames;
+    private final int mTraceThresholdFrameTimeMillis;
     private final ThreadedRendererWrapper mRendererWrapper;
     private final FrameMetricsWrapper mMetricsWrapper;
 
     private long mBeginTime = UNKNOWN_TIMESTAMP;
     private long mEndTime = UNKNOWN_TIMESTAMP;
-    private boolean mShouldTriggerTrace;
     private boolean mSessionEnd;
+    private boolean mCancelled = false;
     private int mTotalFramesCount = 0;
     private int mMissedFramesCount = 0;
     private long mMaxFrameTimeNanos = 0;
 
     private Session mSession;
 
-    /**
-     * Constructor of FrameTracker.
-     * @param session a trace session.
-     * @param handler a handler for handling callbacks.
-     * @param renderer a ThreadedRendererWrapper instance.
-     * @param metrics a FrameMetricsWrapper instance.
-     */
     public FrameTracker(@NonNull Session session, @NonNull Handler handler,
-            @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) {
+            @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics,
+            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis) {
         mSession = session;
         mRendererWrapper = renderer;
         mMetricsWrapper = metrics;
         mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
+        mTraceThresholdMissedFrames = traceThresholdMissedFrames;
+        mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
     }
 
     /**
@@ -109,13 +108,15 @@
         }
         Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
         mRendererWrapper.removeObserver(mObserver);
-        mBeginTime = UNKNOWN_TIMESTAMP;
-        mEndTime = UNKNOWN_TIMESTAMP;
-        mShouldTriggerTrace = false;
+        mCancelled = true;
     }
 
     @Override
     public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+        if (mCancelled) {
+            return;
+        }
+
         // Since this callback might come a little bit late after the end() call.
         // We should keep tracking the begin / end timestamp.
         // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
@@ -136,13 +137,14 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
                     mTotalFramesCount);
             Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
-                    (int) (mMaxFrameTimeNanos / 1_000_000));
+                    (int) (mMaxFrameTimeNanos / NANOS_IN_MILLISECOND));
 
             // Trigger perfetto if necessary.
-            if (mShouldTriggerTrace) {
-                if (DEBUG) {
-                    Log.v(TAG, "Found janky frame, triggering perfetto.");
-                }
+            boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
+                    && mMissedFramesCount >= mTraceThresholdMissedFrames;
+            boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
+                    && mMaxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
+            if (overMissedFramesThreshold || overFrameTimeThreshold) {
                 triggerPerfetto();
             }
             if (mSession.logToStatsd()) {
@@ -168,7 +170,6 @@
 
         if (isJankyFrame) {
             mMissedFramesCount += 1;
-            mShouldTriggerTrace = true;
         }
     }
 
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index fcaa963..771a72c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -36,7 +36,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.Build;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
+import android.provider.DeviceConfig;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
@@ -47,6 +50,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -55,9 +59,21 @@
  */
 public class InteractionJankMonitor {
     private static final String TAG = InteractionJankMonitor.class.getSimpleName();
-    private static final boolean DEBUG = false;
     private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
     private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
+    private static final String SETTINGS_ENABLED_KEY = "enabled";
+    private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+    private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
+            "trace_threshold_missed_frames";
+    private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY =
+            "trace_threshold_frame_time_millis";
+    /** Default to being enabled on debug builds. */
+    private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
+    /** Default to collecting data for all CUJs. */
+    private static final int DEFAULT_SAMPLING_INTERVAL = 1;
+    /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */
+    private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
+    private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -106,12 +122,20 @@
 
     private static volatile InteractionJankMonitor sInstance;
 
+    private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
+            this::updateProperties;
+
     private ThreadedRendererWrapper mRenderer;
     private FrameMetricsWrapper mMetrics;
     private SparseArray<FrameTracker> mRunningTrackers;
     private SparseArray<Runnable> mTimeoutActions;
     private HandlerThread mWorker;
+
     private boolean mInitialized;
+    private boolean mEnabled = DEFAULT_ENABLED;
+    private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
+    private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
+    private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
 
     /** @hide */
     @IntDef({
@@ -134,10 +158,12 @@
             CUJ_NOTIFICATION_APP_START,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface CujType {}
+    public @interface CujType {
+    }
 
     /**
      * Get the singleton of InteractionJankMonitor.
+     *
      * @return instance of InteractionJankMonitor
      */
     public static InteractionJankMonitor getInstance() {
@@ -154,6 +180,7 @@
 
     /**
      * This constructor should be only public to tests.
+     *
      * @param worker the worker thread for the callbacks
      */
     @VisibleForTesting
@@ -165,11 +192,11 @@
 
     /**
      * Init InteractionJankMonitor for later instrumentation.
+     *
      * @param view Any view in the view tree to get context and ThreadedRenderer.
      * @return boolean true if the instance has been initialized successfully.
      */
     public boolean init(@NonNull View view) {
-        //TODO (163505250): This should be no-op if not in droid food rom.
         if (!mInitialized) {
             synchronized (this) {
                 if (!mInitialized) {
@@ -180,7 +207,20 @@
                     mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer());
                     mMetrics = new FrameMetricsWrapper();
                     mWorker.start();
+                    mEnabled = DEFAULT_ENABLED;
+                    mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
                     mInitialized = true;
+
+                    // Post initialization to the background in case we're running on the main
+                    // thread.
+                    mWorker.getThreadHandler().post(
+                            () -> mPropertiesChangedListener.onPropertiesChanged(
+                                    DeviceConfig.getProperties(
+                                            DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR)));
+                    DeviceConfig.addOnPropertiesChangedListener(
+                            DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR,
+                            new HandlerExecutor(mWorker.getThreadHandler()),
+                            mPropertiesChangedListener);
                 }
             }
         }
@@ -189,6 +229,7 @@
 
     /**
      * Create a {@link FrameTracker} instance.
+     *
      * @param session the session associates with this tracker
      * @return instance of the FrameTracker
      */
@@ -196,47 +237,50 @@
     public FrameTracker createFrameTracker(Session session) {
         synchronized (this) {
             if (!mInitialized) return null;
-            return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics);
+            return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics,
+                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
         }
     }
 
     /**
      * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+     *
      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
      * @return boolean true if the tracker is started successfully, false otherwise.
      */
     public boolean begin(@CujType int cujType) {
-        //TODO (163505250): This should be no-op if not in droid food rom.
         synchronized (this) {
-            return begin(cujType, 0L /* timeout */);
+            return begin(cujType, DEFAULT_TIMEOUT_MS);
         }
     }
 
     /**
      * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+     *
      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
      * @param timeout the elapsed time in ms until firing the timeout action.
      * @return boolean true if the tracker is started successfully, false otherwise.
      */
     public boolean begin(@CujType int cujType, long timeout) {
-        //TODO (163505250): This should be no-op if not in droid food rom.
         synchronized (this) {
             if (!mInitialized) {
                 Log.d(TAG, "Not initialized!", new Throwable());
                 return false;
             }
-            Session session = new Session(cujType);
-            FrameTracker tracker = getTracker(session);
+            boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+            if (!mEnabled || !shouldSample) {
+                return false;
+            }
+            FrameTracker tracker = getTracker(cujType);
             // Skip subsequent calls if we already have an ongoing tracing.
             if (tracker != null) return false;
 
             // begin a new trace session.
-            tracker = createFrameTracker(session);
+            tracker = createFrameTracker(new Session(cujType));
             mRunningTrackers.put(cujType, tracker);
             tracker.begin();
 
             // Cancel the trace if we don't get an end() call in specified duration.
-            timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS;
             Runnable timeoutAction = () -> cancel(cujType);
             mTimeoutActions.put(cujType, timeoutAction);
             mWorker.getThreadHandler().postDelayed(timeoutAction, timeout);
@@ -246,6 +290,7 @@
 
     /**
      * End a trace session, must invoke {@link #init(View)} before invoking this method.
+     *
      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
      * @return boolean true if the tracker is ended successfully, false otherwise.
      */
@@ -263,18 +308,18 @@
                 mTimeoutActions.remove(cujType);
             }
 
-            Session session = new Session(cujType);
-            FrameTracker tracker = getTracker(session);
+            FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.end();
-            mRunningTrackers.remove(session.getCuj());
+            mRunningTrackers.remove(cujType);
             return true;
         }
     }
 
     /**
      * Cancel the trace session, must invoke {@link #init(View)} before invoking this method.
+     *
      * @return boolean true if the tracker is cancelled successfully, false otherwise.
      */
     public boolean cancel(@CujType int cujType) {
@@ -291,50 +336,40 @@
                 mTimeoutActions.remove(cujType);
             }
 
-            Session session = new Session(cujType);
-            FrameTracker tracker = getTracker(session);
+            FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.cancel();
-            mRunningTrackers.remove(session.getCuj());
+            mRunningTrackers.remove(cujType);
             return true;
         }
     }
 
-    private void destroy() {
-        synchronized (this) {
-            int trackers = mRunningTrackers.size();
-            for (int i = 0; i < trackers; i++) {
-                mRunningTrackers.valueAt(i).cancel();
-            }
-            mRunningTrackers = null;
-            mTimeoutActions.clear();
-            mTimeoutActions = null;
-            mWorker.quit();
-            mWorker = null;
-        }
-    }
-
-    /**
-     * Abandon current instance.
-     */
-    @VisibleForTesting
-    public static void abandon() {
-        if (sInstance == null) return;
-        synchronized (InteractionJankMonitor.class) {
-            if (sInstance == null) return;
-            sInstance.destroy();
-            sInstance = null;
-        }
-    }
-
-    private FrameTracker getTracker(Session session) {
+    private FrameTracker getTracker(@CujType int cuj) {
         synchronized (this) {
             if (!mInitialized) return null;
-            return mRunningTrackers.get(session.getCuj());
+            return mRunningTrackers.get(cuj);
         }
     }
 
+    private void updateProperties(DeviceConfig.Properties properties) {
+        synchronized (this) {
+            mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+                    DEFAULT_SAMPLING_INTERVAL);
+            mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+            mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
+                    DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+            mTraceThresholdFrameTimeMillis = properties.getInt(
+                    SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
+                    DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+        }
+    }
+
+    @VisibleForTesting
+    public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() {
+        return mPropertiesChangedListener;
+    }
+
     /**
      * Trigger the perfetto daemon to collect and upload data.
      */
@@ -402,12 +437,14 @@
      * A class to represent a session.
      */
     public static class Session {
-        private @CujType int mCujType;
+        @CujType
+        private int mCujType;
 
         public Session(@CujType int cujType) {
             mCujType = cujType;
         }
 
+        @CujType
         public int getCuj() {
             return mCujType;
         }
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 41be5c4..2237efc 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -57,6 +57,7 @@
     public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
     public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
     public static String DO_NOT_DISTURB = "DO_NOT_DISTURB";
+    public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
 
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -191,6 +192,13 @@
                 NotificationManager.IMPORTANCE_LOW);
         channelsList.add(dndChanges);
 
+        final NotificationChannel newFeaturePrompt = new NotificationChannel(
+                ACCESSIBILITY_MAGNIFICATION,
+                context.getString(R.string.notification_channel_accessibility_magnification),
+                NotificationManager.IMPORTANCE_HIGH);
+        newFeaturePrompt.setBlockable(true);
+        channelsList.add(newFeaturePrompt);
+
         nm.createNotificationChannels(channelsList);
     }
 
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c110b26..c85b5d7 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -18,9 +18,10 @@
 package com.android.internal.os;
 
 import android.compat.annotation.UnsupportedAppUsage;
-import android.os.BasicShellCommandHandler;
 import android.os.Build;
 
+import com.android.modules.utils.BasicShellCommandHandler;
+
 import java.io.PrintStream;
 
 public abstract class BaseCommand {
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 2676745..9e59e50 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -203,7 +203,7 @@
             }
         }
         return getStats(IBatteryStats.Stub.asInterface(
-                ServiceManager.getService(BatteryStats.SERVICE_NAME)));
+                ServiceManager.getService(BatteryStats.SERVICE_NAME)), true);
     }
 
     @UnsupportedAppUsage
@@ -223,8 +223,13 @@
 
     @UnsupportedAppUsage
     public BatteryStats getStats() {
+        return getStats(true /* updateAll */);
+    }
+
+    /** Retrieves stats from BatteryService, optionally getting updated numbers */
+    public BatteryStats getStats(boolean updateAll) {
         if (mStats == null) {
-            load();
+            load(updateAll);
         }
         return mStats;
     }
@@ -720,19 +725,23 @@
 
     @UnsupportedAppUsage
     private void load() {
+        load(true);
+    }
+
+    private void load(boolean updateAll) {
         if (mBatteryInfo == null) {
             return;
         }
-        mStats = getStats(mBatteryInfo);
+        mStats = getStats(mBatteryInfo, updateAll);
         if (mCollectBatteryBroadcast) {
             mBatteryBroadcast = mContext.registerReceiver(null,
                     new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
         }
     }
 
-    private static BatteryStatsImpl getStats(IBatteryStats service) {
+    private static BatteryStatsImpl getStats(IBatteryStats service, boolean updateAll) {
         try {
-            ParcelFileDescriptor pfd = service.getStatisticsStream();
+            ParcelFileDescriptor pfd = service.getStatisticsStream(updateAll);
             if (pfd != null) {
                 if (false) {
                     Log.d(TAG, "selinux context: "
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 70b1ad4..aa42979 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -56,6 +56,7 @@
     public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000;
     public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false;
     public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true;
+    public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false;
     public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500;
     private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
 
@@ -95,6 +96,7 @@
     private boolean mAddDebugEntries = false;
     private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID;
     private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE;
+    private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS;
 
     private CachedDeviceState.Readonly mDeviceState;
     private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
@@ -185,8 +187,7 @@
     public CallSession callStarted(Binder binder, int code, int workSourceUid) {
         noteNativeThreadId();
 
-        if (!mRecordingAllTransactionsForUid
-                && (mDeviceState == null || mDeviceState.isCharging())) {
+        if (!canCollect()) {
             return null;
         }
 
@@ -255,8 +256,7 @@
 
         synchronized (mLock) {
             // This was already checked in #callStart but check again while synchronized.
-            if (!mRecordingAllTransactionsForUid
-                    && (mDeviceState == null || mDeviceState.isCharging())) {
+            if (!canCollect()) {
                 return;
             }
 
@@ -372,6 +372,22 @@
         mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids());
     }
 
+    private boolean canCollect() {
+        if (mRecordingAllTransactionsForUid) {
+            return true;
+        }
+        if (mIgnoreBatteryStatus) {
+            return true;
+        }
+        if (mDeviceState == null) {
+            return false;
+        }
+        if (mDeviceState.isCharging()) {
+            return false;
+        }
+        return true;
+    }
+
     /**
      * This method is expensive to call.
      */
@@ -672,6 +688,18 @@
     }
 
     /**
+     * Whether to ignore battery status when collecting stats
+     */
+    public void setIgnoreBatteryStatus(boolean ignored) {
+        synchronized (mLock) {
+            if (ignored != mIgnoreBatteryStatus) {
+                mIgnoreBatteryStatus = ignored;
+                reset();
+            }
+        }
+    }
+
+    /**
      * Marks the specified work source UID for total binder call tracking: detailed information
      * will be recorded for all calls from this source ID.
      *
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 932ff57..2805dcc 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -40,6 +40,7 @@
     public static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
     private static final int SESSION_POOL_SIZE = 50;
     private static final boolean DISABLED_SCREEN_STATE_TRACKING_VALUE = false;
+    public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false;
 
     @GuardedBy("mLock")
     private final SparseArray<Entry> mEntries = new SparseArray<>(512);
@@ -56,6 +57,7 @@
     private long mStartElapsedTime = SystemClock.elapsedRealtime();
     private boolean mAddDebugEntries = false;
     private boolean mTrackScreenInteractive = false;
+    private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS;
 
     public LooperStats(int samplingInterval, int entriesSizeCap) {
         this.mSamplingInterval = samplingInterval;
@@ -139,8 +141,16 @@
     }
 
     private boolean deviceStateAllowsCollection() {
-        // Do not collect data if on charger or the state is not set.
-        return mDeviceState != null && !mDeviceState.isCharging();
+        if (mIgnoreBatteryStatus) {
+            return true;
+        }
+        if (mDeviceState == null) {
+            return false;
+        }
+        if (mDeviceState.isCharging()) {
+            return false;
+        }
+        return true;
     }
 
     /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
@@ -225,6 +235,10 @@
         mTrackScreenInteractive = enabled;
     }
 
+    public void setIgnoreBatteryStatus(boolean ignore) {
+        mIgnoreBatteryStatus = ignore;
+    }
+
     @Nullable
     private Entry findEntry(Message msg, boolean allowCreateNew) {
         final boolean isInteractive = mTrackScreenInteractive
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 854fb17..44dca9b 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -69,6 +69,6 @@
     void onRegistrationFailed(in CellIdentity cellIdentity,
              String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
     void onBarringInfoChanged(in BarringInfo barringInfo);
-    void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
-    void onDataEnabledChanged(boolean enabled, int reason);
+    void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
+
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index ae1657b..a28a663 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -41,10 +41,16 @@
             IOnSubscriptionsChangedListener callback);
     void removeOnSubscriptionsChangedListener(String pkg,
             IOnSubscriptionsChangedListener callback);
-
-    void listenWithEventList(in int subId, String pkg, String featureId,
-            IPhoneStateListener callback, in int[] events, boolean notifyNow);
-
+    /**
+      * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int,
+      * boolean) instead
+      */
+    @UnsupportedAppUsage
+    void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
+    void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, long events,
+            boolean notifyNow);
+    void listenForSubscriber(in int subId, String pkg, String featureId,
+            IPhoneStateListener callback, long events, boolean notifyNow);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void notifyCallStateForAllSubs(int state, String incomingNumber);
     void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber);
@@ -93,6 +99,6 @@
     void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
             String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
     void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
-    void notifyPhysicalChannelConfigForSubscriber(in int subId,
+    void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
             in List<PhysicalChannelConfig> configs);
 }
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index a87e8aa..8012540 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -15,15 +15,11 @@
 package com.android.internal.util;
 
 import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Build;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.util.EventLog;
-import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.SparseLongArray;
 
@@ -135,8 +131,16 @@
         mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
 
         // Post initialization to the background in case we're running on the main thread.
-        BackgroundThread.getHandler().post(this::registerSettingsObserver);
-        BackgroundThread.getHandler().post(this::readSettings);
+        BackgroundThread.getHandler().post(() -> this.updateProperties(
+                DeviceConfig.getProperties(DeviceConfig.NAMESPACE_LATENCY_TRACKER)));
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+                BackgroundThread.getExecutor(), this::updateProperties);
+    }
+
+    private void updateProperties(DeviceConfig.Properties properties) {
+        mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+                DEFAULT_SAMPLING_INTERVAL);
+        mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
     }
 
     /**
@@ -171,28 +175,6 @@
         }
     }
 
-    private void registerSettingsObserver() {
-        Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
-        mContext.getContentResolver().registerContentObserver(
-                settingsUri, false, new SettingsObserver(this), UserHandle.myUserId());
-    }
-
-    private void readSettings() {
-        KeyValueListParser parser = new KeyValueListParser(',');
-        String settingsValue = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.LATENCY_TRACKER);
-
-        try {
-            parser.setString(settingsValue);
-            mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
-                    DEFAULT_SAMPLING_INTERVAL);
-            mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Incorrect settings format", e);
-            mEnabled = false;
-        }
-    }
-
     public static boolean isEnabled(Context ctx) {
         return getInstance(ctx).isEnabled();
     }
@@ -236,8 +218,8 @@
     /**
      * Logs an action that has started and ended. This needs to be called from the main thread.
      *
-     * @param action          The action to end. One of the ACTION_* values.
-     * @param duration        The duration of the action in ms.
+     * @param action   The action to end. One of the ACTION_* values.
+     * @param duration The duration of the action in ms.
      */
     public void logAction(int action, int duration) {
         boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
@@ -260,18 +242,4 @@
                     FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
         }
     }
-
-    private static class SettingsObserver extends ContentObserver {
-        private final LatencyTracker mThisTracker;
-
-        SettingsObserver(LatencyTracker thisTracker) {
-            super(BackgroundThread.getHandler());
-            mThisTracker = thisTracker;
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            mThisTracker.readSettings();
-        }
-    }
 }
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 48f33a6..781895e 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -20,6 +20,8 @@
 
 #include "android_os_HwBinder.h"
 
+#include "android_util_Binder.h" // for binder_report_exception
+
 #include "android_os_HwParcel.h"
 #include "android_os_HwRemoteBinder.h"
 
@@ -183,15 +185,7 @@
         env->ExceptionDescribe();
         env->ExceptionClear();
 
-        // It is illegal to call IsInstanceOf if there is a pending exception.
-        // Attempting to do so results in a JniAbort which crashes the entire process.
-        if (env->IsInstanceOf(excep, gErrorClass)) {
-            /* It's an error */
-            LOG(ERROR) << "Forcefully exiting";
-            _exit(1);
-        } else {
-            LOG(ERROR) << "Uncaught exception!";
-        }
+        binder_report_exception(env, excep, "Uncaught error or exception in hwbinder!");
 
         env->DeleteLocalRef(excep);
     }
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a3cb4c0..7c9b862 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -304,8 +304,9 @@
     report_java_lang_error_fatal_error(env, error, msg);
 }
 
-static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
-{
+namespace android {
+
+void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg) {
     env->ExceptionClear();
 
     ScopedLocalRef<jstring> tagstr(env, env->NewStringUTF(LOG_TAG));
@@ -333,6 +334,8 @@
     }
 }
 
+} // namespace android
+
 class JavaBBinderHolder;
 
 class JavaBBinder : public BBinder
@@ -407,9 +410,9 @@
 
         if (env->ExceptionCheck()) {
             ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
-            report_exception(env, excep.get(),
-                "*** Uncaught remote exception!  "
-                "(Exceptions are not yet supported across processes.)");
+            binder_report_exception(env, excep.get(),
+                                    "*** Uncaught remote exception!  "
+                                    "(Exceptions are not yet supported across processes.)");
             res = JNI_FALSE;
         }
 
@@ -423,8 +426,8 @@
 
         if (env->ExceptionCheck()) {
             ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
-            report_exception(env, excep.get(),
-                "*** Uncaught exception in onBinderStrictModePolicyChange");
+            binder_report_exception(env, excep.get(),
+                                    "*** Uncaught exception in onBinderStrictModePolicyChange");
         }
 
         // Need to always call through the native implementation of
@@ -569,8 +572,8 @@
                                       jBinderProxy.get());
             if (env->ExceptionCheck()) {
                 jthrowable excep = env->ExceptionOccurred();
-                report_exception(env, excep,
-                        "*** Uncaught exception returned from death notification!");
+                binder_report_exception(env, excep,
+                                        "*** Uncaught exception returned from death notification!");
             }
 
             // Serialize with our containing DeathRecipientList so that we can't
@@ -1163,8 +1166,8 @@
 
     if (env->ExceptionCheck()) {
         ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
-        report_exception(env, excep.get(),
-            "*** Uncaught exception in binderProxyLimitCallbackFromNative");
+        binder_report_exception(env, excep.get(),
+                                "*** Uncaught exception in binderProxyLimitCallbackFromNative");
     }
 }
 
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index c109d6c..9098d46 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -35,6 +35,8 @@
 extern void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
         bool canThrowRemoteException = false, int parcelSize = 0);
 
+// does not take ownership of the exception, aborts if this is an error
+void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg);
 }
 
 #endif
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 1ea918a..1c78750 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -50,6 +50,7 @@
 
     jmethodID dispatchInputEvent;
     jmethodID onFocusEvent;
+    jmethodID onPointerCaptureEvent;
     jmethodID onBatchedInputEventPending;
 } gInputEventReceiverClassInfo;
 
@@ -345,6 +346,19 @@
                 finishInputEvent(seq, true /* handled */);
                 continue;
             }
+            case AINPUT_EVENT_TYPE_CAPTURE: {
+                const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(inputEvent);
+                if (kDebugDispatchCycle) {
+                    ALOGD("channel '%s' ~ Received capture event: pointerCaptureEnabled=%s",
+                          getInputChannelName().c_str(),
+                          toString(captureEvent->getPointerCaptureEnabled()));
+                }
+                env->CallVoidMethod(receiverObj.get(),
+                                    gInputEventReceiverClassInfo.onPointerCaptureEvent,
+                                    jboolean(captureEvent->getPointerCaptureEnabled()));
+                finishInputEvent(seq, true /* handled */);
+                continue;
+            }
 
             default:
                 assert(false); // InputConsumer should prevent this from ever happening
@@ -489,6 +503,9 @@
             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
     gInputEventReceiverClassInfo.onFocusEvent =
             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
+    gInputEventReceiverClassInfo.onPointerCaptureEvent =
+            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
+                             "(Z)V");
     gInputEventReceiverClassInfo.onBatchedInputEventPending =
             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
                              "(I)V");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c1f4b82..3156f71 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+/*
+ * Disable optimization of this file if we are compiling with the address
+ * sanitizer.  This is a mitigation for b/122921367 and can be removed once the
+ * bug is fixed.
+ */
+#if __has_feature(address_sanitizer)
+#pragma clang optimize off
+#endif
+
 #define LOG_TAG "Zygote"
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 
@@ -69,7 +78,6 @@
 #include <android-base/unique_fd.h>
 #include <bionic/malloc.h>
 #include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
 #include <cutils/fs.h>
 #include <cutils/memory.h>
 #include <cutils/multiuser.h>
@@ -1641,28 +1649,6 @@
   }
 }
 
-#ifdef ANDROID_EXPERIMENTAL_MTE
-static void SetTagCheckingLevel(int level) {
-#ifdef __aarch64__
-  if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
-    return;
-  }
-
-  int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
-  if (tagged_addr_ctrl < 0) {
-    ALOGE("prctl(PR_GET_TAGGED_ADDR_CTRL) failed: %s", strerror(errno));
-    return;
-  }
-
-  tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
-  if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
-    ALOGE("prctl(PR_SET_TAGGED_ADDR_CTRL, %d) failed: %s", tagged_addr_ctrl,
-          strerror(errno));
-  }
-#endif
-}
-#endif
-
 // Utility routine to specialize a zygote child process.
 static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
                              jint runtime_flags, jobjectArray rlimits,
@@ -1798,22 +1784,14 @@
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
       break;
     case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
-#ifdef ANDROID_EXPERIMENTAL_MTE
-      SetTagCheckingLevel(PR_MTE_TCF_ASYNC);
-#endif
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
       break;
     case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
-#ifdef ANDROID_EXPERIMENTAL_MTE
-      SetTagCheckingLevel(PR_MTE_TCF_SYNC);
-#endif
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
       break;
     default:
-#ifdef ANDROID_EXPERIMENTAL_MTE
-      SetTagCheckingLevel(PR_MTE_TCF_NONE);
-#endif
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+      break;
   }
   android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
   // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 32a40d9..0453d3f3 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -381,7 +381,7 @@
     optional int32 requested_width = 18;
     optional int32 requested_height = 19;
     optional int32 view_visibility = 20 [(.android.typedef) = "android.view.View.Visibility"];
-    optional int32 system_ui_visibility = 21;
+    optional int32 system_ui_visibility = 21 [deprecated=true];
     optional bool has_surface = 22;
     optional bool is_ready_for_display = 23;
     optional .android.graphics.RectProto display_frame = 24 [deprecated=true];
diff --git a/core/proto/android/stats/camera/Android.bp b/core/proto/android/stats/camera/Android.bp
new file mode 100644
index 0000000..cc75e57
--- /dev/null
+++ b/core/proto/android/stats/camera/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+java_library {
+    name: "cameraprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "*.proto",
+    ],
+    java_version: "1.8",
+    target: {
+        android: {
+            jarjar_rules: "jarjar-rules.txt",
+        },
+        host: {
+            static_libs: ["libprotobuf-java-nano"],
+        }
+    },
+    sdk_version: "core_platform",
+}
diff --git a/core/proto/android/stats/camera/camera.proto b/core/proto/android/stats/camera/camera.proto
new file mode 100644
index 0000000..4062855
--- /dev/null
+++ b/core/proto/android/stats/camera/camera.proto
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.stats.camera;
+option java_multiple_files = true;
+
+message CameraStreamProto {
+    // The stream width (in pixels)
+    optional int32 width = 1;
+    // The stream height (in pixels)
+    optional int32 height = 2;
+    // The format of the stream
+    optional int32 format = 3;
+    // The dataspace of the stream
+    optional int32 data_space = 4;
+    // The usage flag of the stream
+    optional int64 usage = 5;
+
+    // The number of requests for this stream
+    optional int64 request_count = 6;
+    // The number of buffer error for this stream
+    optional int64 error_count = 7;
+    // The capture latency of first request for this stream
+    optional int32 first_capture_latency_millis = 8;
+
+    // The maximum number of hal buffers
+    optional int32 max_hal_buffers = 9;
+    // The maximum number of app buffers
+    optional int32 max_app_buffers = 10;
+}
diff --git a/core/proto/android/stats/camera/jarjar-rules.txt b/core/proto/android/stats/camera/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/camera/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index 9d49126..eb8a3b1 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -166,12 +166,23 @@
  * Logged from:
  *   frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
  *   frameworks/av/services/mediaanalytics/statsd_extractor.cpp
- * Next Tag: 4
+ * Next Tag: 5
  */
 message ExtractorData {
     optional string format = 1;
     optional string mime = 2;
     optional int32 tracks = 3;
+
+    enum EntryPoint {
+        UNSET = 0; // For backwards compatibility with clients that don't
+                   // collect the entry point.
+        SDK = 1;
+        NDK_WITH_JVM = 2;
+        NDK_NO_JVM = 3;
+        OTHER = 4; // For extractor users that don't make use of the APIs.
+    }
+
+    optional EntryPoint entry_point = 4 [default = UNSET];
 }
 
 /**
diff --git a/core/proto/android/stats/tv/tif_enums.proto b/core/proto/android/stats/tv/tif_enums.proto
new file mode 100644
index 0000000..a9028e5
--- /dev/null
+++ b/core/proto/android/stats/tv/tif_enums.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.stats.tv;
+option java_multiple_files = true;
+
+// Enums for TV Input Framework
+option java_outer_classname = "TifStatsEnums";
+
+// Tune State of a TV Input Service Framework
+enum TifTuneState {
+  TIF_TUNE_STATE_UNKNOWN = 0;
+  CREATED = 1;
+  SURFACE_ATTACHED = 2;
+  SURFACE_DETACHED = 3;
+  RELEASED = 4;
+  TUNE_STARTED = 5;
+  VIDEO_AVAILABLE = 6;
+
+  // Keep in sync with TvInputManager
+  // Use the TvInputManager value + 100
+  VIDEO_UNAVAILABLE_REASON_UNKNOWN = 100;
+  VIDEO_UNAVAILABLE_REASON_TUNING = 101;
+  VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 102;
+  VIDEO_UNAVAILABLE_REASON_BUFFERING = 103;
+  VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 104;
+  VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 105;
+  VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 106;
+  VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION=107;
+  VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED=108;
+  VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE=109;
+  VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED=110;
+  VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION=111;
+  VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING=112;
+  VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD=113;
+  VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE=114;
+  VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID=115;
+  VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT=116;
+  VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING=117;
+  VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN=118;
+}
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 552a1bd..9662b8e 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -28,7 +28,6 @@
         android:layout_height="wrap_content"
         android:gravity="end"
         android:orientation="horizontal"
-        android:paddingEnd="@dimen/bubble_gone_padding_end"
         android:background="@color/notification_action_list_background_color"
         >
 
@@ -45,22 +44,34 @@
             <!-- actions will be added here -->
         </com.android.internal.widget.NotificationActionListLayout>
 
-        <ImageView
-            android:id="@+id/snooze_button"
-            android:layout_width="@dimen/notification_actions_icon_size"
-            android:layout_height="@dimen/notification_actions_icon_size"
-            android:layout_gravity="center_vertical|end"
-            android:visibility="gone"
-            android:scaleType="centerInside"
-            />
+        <!--
+        This nested linear layout exists to ensure that if the neither of the contained
+        actions is visible we have some minimum padding at the end of the actions is present,
+        then there will be 12dp of padding at the end of the actions list.
+        -->
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:minWidth="@dimen/snooze_and_bubble_gone_padding_end"
+            >
+            <ImageView
+                android:id="@+id/snooze_button"
+                android:layout_width="@dimen/notification_actions_icon_size"
+                android:layout_height="@dimen/notification_actions_icon_size"
+                android:layout_gravity="center_vertical|end"
+                android:visibility="gone"
+                android:scaleType="centerInside"
+                />
 
-        <ImageView
-            android:id="@+id/bubble_button"
-            android:layout_width="@dimen/notification_actions_icon_size"
-            android:layout_height="@dimen/notification_actions_icon_size"
-            android:layout_gravity="center_vertical|end"
-            android:visibility="gone"
-            android:scaleType="centerInside"
-            />
+            <ImageView
+                android:id="@+id/bubble_button"
+                android:layout_width="@dimen/notification_actions_icon_size"
+                android:layout_height="@dimen/notification_actions_icon_size"
+                android:layout_gravity="center_vertical|end"
+                android:visibility="gone"
+                android:scaleType="centerInside"
+                />
+        </LinearLayout>
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index a26473a..b0ee12a 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -50,20 +50,18 @@
         android:theme="@style/Theme.DeviceDefault.Notification"
         >
 
-        <TextView
-            android:id="@+id/app_name_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/notification_header_separating_margin"
-            android:singleLine="true"
-            android:textAppearance="?attr/notificationHeaderTextAppearance"
-            android:visibility="?attr/notificationHeaderAppNameVisibility"
-            />
-
         <include layout="@layout/notification_top_line_views" />
 
     </NotificationTopLineView>
 
+    <FrameLayout
+        android:id="@+id/alternate_expand_target"
+        android:layout_width="@dimen/notification_content_margin_start"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:importantForAccessibility="no"
+        />
+
     <com.android.internal.widget.NotificationExpandButton
         android:id="@+id/expand_button"
         android:layout_width="@dimen/notification_header_expand_icon_size"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index ded16b7..69d4a12 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -68,6 +68,12 @@
             android:theme="@style/Theme.DeviceDefault.Notification"
             >
 
+            <!--
+            NOTE: The notification_top_line_views layout contains the app_name_text.
+            In order to include the title view at the beginning, the Notification.Builder
+            has logic to hide that view whenever this title view is to be visible.
+            -->
+
             <TextView
                 android:id="@+id/title"
                 android:layout_width="wrap_content"
@@ -138,6 +144,14 @@
         />
 
     <FrameLayout
+        android:id="@+id/alternate_expand_target"
+        android:layout_width="@dimen/notification_content_margin_start"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:importantForAccessibility="no"
+        />
+
+    <FrameLayout
         android:id="@+id/expand_button_touch_container"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 4cf323b..696cb65 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -50,16 +50,56 @@
             android:id="@+id/notification_main_column"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:layout_marginTop="46dp"
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginBottom="@dimen/notification_content_margin"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:orientation="vertical"
             >
+            <!-- TODO(b/172652345): fix the media style -->
+            <!--<include layout="@layout/notification_template_part_line1"/>-->
+            <!--<include layout="@layout/notification_template_text"/>-->
 
-            <include layout="@layout/notification_template_part_line1" />
+            <LinearLayout
+                android:id="@+id/line1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+                <TextView android:id="@+id/title"
+                    android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:singleLine="true"
+                    android:ellipsize="marquee"
+                    android:fadingEdge="horizontal"
+                    android:textAlignment="viewStart"
+                    />
+                <TextView android:id="@+id/text_line_1"
+                    style="@style/Widget.DeviceDefault.Notification.Text"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="end|bottom"
+                    android:layout_marginStart="16dp"
+                    android:singleLine="true"
+                    android:ellipsize="marquee"
+                    android:fadingEdge="horizontal"
+                    />
+            </LinearLayout>
 
-            <include layout="@layout/notification_template_text" />
+            <com.android.internal.widget.ImageFloatingTextView
+                style="@style/Widget.DeviceDefault.Notification.Text"
+                android:id="@+id/text"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/notification_text_height"
+                android:layout_gravity="top"
+                android:layout_marginTop="0.5dp"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"
+                android:gravity="top"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                />
         </LinearLayout>
 
         <LinearLayout
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 520ae28..f9364d5 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -327,10 +327,10 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="@dimen/conversation_expand_button_size"
-            android:paddingStart="16dp"
+            android:paddingStart="@dimen/conversation_expand_button_side_margin"
             android:orientation="horizontal"
             android:layout_gravity="end|top"
-            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:paddingEnd="@dimen/conversation_expand_button_side_margin"
             android:clipToPadding="false"
             android:clipChildren="false"
             >
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 52053dc..7daccd2 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -45,7 +45,7 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:layout_marginStart="@dimen/notification_content_margin_start"
-            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:layout_marginTop="46dp"
             android:layout_alignParentTop="true"
             android:tag="media"
             >
@@ -58,8 +58,50 @@
                 android:paddingBottom="@dimen/notification_content_margin"
                 android:orientation="vertical"
                 >
-                <include layout="@layout/notification_template_part_line1"/>
-                <include layout="@layout/notification_template_text"/>
+                <!-- TODO(b/172652345): fix the media style -->
+                <!--<include layout="@layout/notification_template_part_line1"/>-->
+                <!--<include layout="@layout/notification_template_text"/>-->
+
+                <LinearLayout
+                    android:id="@+id/line1"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                    <TextView android:id="@+id/title"
+                        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:ellipsize="marquee"
+                        android:fadingEdge="horizontal"
+                        android:textAlignment="viewStart"
+                        />
+                    <TextView android:id="@+id/text_line_1"
+                        style="@style/Widget.DeviceDefault.Notification.Text"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:gravity="end|bottom"
+                        android:layout_marginStart="16dp"
+                        android:singleLine="true"
+                        android:ellipsize="marquee"
+                        android:fadingEdge="horizontal"
+                        />
+                </LinearLayout>
+
+                <com.android.internal.widget.ImageFloatingTextView
+                    style="@style/Widget.DeviceDefault.Notification.Text"
+                    android:id="@+id/text"
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_text_height"
+                    android:layout_gravity="top"
+                    android:layout_marginTop="0.5dp"
+                    android:ellipsize="marquee"
+                    android:fadingEdge="horizontal"
+                    android:gravity="top"
+                    android:singleLine="true"
+                    android:textAlignment="viewStart"
+                    />
             </LinearLayout>
             <LinearLayout
                 android:id="@+id/media_actions"
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index 51974ac..c71e886 100644
--- a/core/res/res/layout/notification_top_line_views.xml
+++ b/core/res/res/layout/notification_top_line_views.xml
@@ -14,13 +14,23 @@
   ~ limitations under the License
   -->
 <!--
- This layout file should be included inside a NotificationTopLineView, usually after either a
- <TextView android:id="@+id/app_name_text"/> or <TextView android:id="@+id/title"/>
+ This layout file should be included inside a NotificationTopLineView, sometimes after a
+ <TextView android:id="@+id/title"/>
 -->
 <merge
     xmlns:android="http://schemas.android.com/apk/res/android">
 
     <TextView
+        android:id="@+id/app_name_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:singleLine="true"
+        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:visibility="?attr/notificationHeaderAppNameVisibility"
+        />
+
+    <TextView
         android:id="@+id/header_text_secondary_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 31993fb..171d896 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Program loop tans"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programme wat batterykrag gebruik"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruik tans batterykrag"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> programme gebruik tans batterykrag"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tik vir besonderhede oor battery- en datagebruik"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index dee3521..5871fe2 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"የዩኤስቢ ግንኙነት"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP እየሠራ ነው"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ባትሪ በመፍጀት ላይ ያሉ መተግበሪያዎች"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ባትሪ እየተጠቀመ ነው"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> መተግበሪያዎች ባትሪ እየተጠቀሙ ነው"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"በባትሪ እና ውሂብ አጠቃቀም ላይ ዝርዝሮችን ለማግኘት መታ ያድርጉ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 905d2dd..b578769 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -299,6 +299,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"‏اتصال USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"التطبيق قيد التشغيل"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"التطبيقات التي تستهلك البطارية"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"يستخدم تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> البطارية"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"تستخدم <xliff:g id="NUMBER">%1$d</xliff:g> من التطبيقات البطارية"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"انقر للحصول على تفاصيل حول البطارية واستخدام البيانات"</string>
@@ -2324,4 +2326,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index e58274c..f1b0d84 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"ইউএছবি সংযোগ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"এপ্ চলি আছে"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"বেটাৰি খৰচ কৰা এপসমূহ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টা এপে বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 499cd20..01fb48e 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB əlaqə"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tətbiq işləyir"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareyadan istifadə edən tətbiqlər"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> batareyadan istifadə edir"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> tətbiq batareyadan istifadə edir"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya və data istifadəsi haqqında ətraflı məlumat üçün klikləyin"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 5e26453..bfbd3e4 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -123,28 +123,28 @@
     <string name="roamingText11" msgid="5245687407203281407">"Baner rominga je uključen"</string>
     <string name="roamingText12" msgid="673537506362152640">"Baner rominga je isključen"</string>
     <string name="roamingTextSearching" msgid="5323235489657753486">"Pretraživanje usluge"</string>
-    <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-ja nije uspelo"</string>
+    <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-a nije uspelo"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-ja. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-a, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-a. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"Problem u vezi sa registrovanjem pozivanja preko Wi‑Fi-ja kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
   </string-array>
     <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
     <skip />
-    <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-ja"</string>
-    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-ja"</string>
+    <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-a"</string>
+    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-a"</string>
     <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN poziv"</string>
     <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string>
     <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
-    <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
+    <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-a | <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
-    <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-ja"</string>
+    <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-a"</string>
     <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string>
-    <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-ja"</string>
+    <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-a"</string>
     <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
     <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Isključeno"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-ja"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-a"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv preko mobilne mreže"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aktivna aplikacija"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije koje troše bateriju"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi bateriju"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikacije (<xliff:g id="NUMBER">%1$d</xliff:g>) koriste bateriju"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o bateriji i potrošnji podataka"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d6a7257..c4a3a7e 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Падключэнне USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Праграма працуе"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Праграмы, якія выкарыстоўваюць акумулятар"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> выкарыстоўвае акумулятар"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Наступная колькасць праграм выкарыстоўваюць акумулятар: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Дакраніцеся, каб даведацца пра выкарыстанне трафіка і акумулятара"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 376ef264..07f1ce7 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB връзка"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложението работи"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, използващи батерията"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> използва батерията"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> приложения използват батерията"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Докоснете за информация относно използването на батерията и преноса на данни"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3b92f87..9b85fc7 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB সংযোগ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"অ্যাপ চলছে"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"কিছু অ্যাপ ব্যাটারি ব্যবহার করছে"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপটি ব্যাটারি ব্যবহার করছে"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টি অ্যাপ ব্যাটারি ব্যবহার করছে"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ব্যাটারি এবং ডেটার ব্যবহারের বিশদ বিবরণের জন্য ট্যাপ করুন"</string>
@@ -802,7 +804,7 @@
     <string name="orgTypeOther" msgid="5450675258408005553">"অন্যান্য"</string>
     <string name="orgTypeCustom" msgid="1126322047677329218">"কাস্টম"</string>
     <string name="relationTypeCustom" msgid="282938315217441351">"কাস্টম"</string>
-    <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+    <string name="relationTypeAssistant" msgid="4057605157116589315">"অ্যাসিস্ট্যান্ট"</string>
     <string name="relationTypeBrother" msgid="7141662427379247820">"ভাই"</string>
     <string name="relationTypeChild" msgid="9076258911292693601">"সন্তান"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"জীবনসাথি"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f180df6..3d2249b 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Pokrenuta aplikacija"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije koje troše bateriju"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> troši bateriju"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Broj aplikacija koje troše bateriju: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4d45791..f1845e3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Connexió USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicació en execució"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacions que consumeixen bateria"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> està consumint bateria"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacions estan consumint bateria"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca per obtenir informació sobre l\'ús de dades i de bateria"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index e9f1fad..42d5636 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Připojení USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikace je spuštěna"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikace spotřebovávají baterii"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> využívá baterii"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikace (<xliff:g id="NUMBER">%1$d</xliff:g>) využívají baterii"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 75bdda0..0487406 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-forbindelse"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Appen kører"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps, der bruger batteri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruger batteri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps bruger batteri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryk for at se info om batteri- og dataforbrug"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 6d82b62..ebbde3c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-Verbindung"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App wird ausgeführt"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Strom verbrauchende Apps"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> verbraucht Strom"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> Apps verbrauchen Strom"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Für Details zur Akku- und Datennutzung tippen"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 42513aa..306d36e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Σύνδεση USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Η εφαρμογή εκτελείται"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Εφαρμογές που καταναλώνουν μπαταρία"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> χρησιμοποιεί μπαταρία"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> εφαρμογές χρησιμοποιούν μπαταρία"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Πατήστε για λεπτομέρειες σχετικά με τη χρήση μπαταρίας και δεδομένων"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index ef5a92a..a8706cb 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6dae39b..57e2eae 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 62e42b3..33ffbe3 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8579628..f5be6fd 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string>
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6674ecd..cd3ff36 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎USB connection‎‏‎‎‏‎"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎App running‎‏‎‎‏‎"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎Apps consuming battery‎‏‎‎‏‎"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎Magnification‎‏‎‎‏‎"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is using battery‎‏‎‎‏‎"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="NUMBER">%1$d</xliff:g>‎‏‎‎‏‏‏‎ apps are using battery‎‏‎‎‏‎"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎Tap for details on battery and data usage‎‏‎‎‏‎"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎‎‎‎‎New: Window Magnifier‎‏‎‎‏‎"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎You can now magnify some or all of your screen‎‏‎‎‏‎"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎Turn on in Settings‎‏‎‎‏‎"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎Dismiss‎‏‎‎‏‎"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a7e2994..2d521b3 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Conexión USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App en ejecución"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que consumen batería"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está consumiendo batería"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps están consumiendo batería"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Presiona para obtener información sobre el uso de datos y de la batería"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0d8fc2b..abc6946 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Conexión USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicación en ejecución"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicaciones que consumen batería"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando la batería"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicaciones están usando la batería"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para ver información detallada sobre el uso de datos y de la batería"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 7a0a0ed..130ef6e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-ühendus"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Rakendus töötab"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Rakendused kasutavad akutoidet"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> kasutab akutoidet"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> rakendust kasutab akutoidet"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Aku ja andmekasutuse üksikasjade nägemiseks puudutage"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a00282f..5273a48 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB konexioa"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikazio bat abian da"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Bateria kontsumitzen ari diren aplikazioak"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ari da bateria erabiltzen"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikazio ari dira bateria erabiltzen"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Sakatu bateria eta datuen erabilerari buruzko xehetasunak ikusteko"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fb0ec66..7596bab 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"‏اتصال USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"برنامه درحال اجرا"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"برنامه‌های مصرف‌کننده باتری"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال استفاده کردن از باتری است"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> برنامه درحال استفاده کردن از باتری هستند"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 27abeea..d337315 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-yhteys"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Sovellus käynnissä"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkua kuluttavat sovellukset"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> käyttää akkua."</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> sovellusta käyttää akkua."</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Katso lisätietoja akun ja datan käytöstä napauttamalla."</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index c09c71e..d2c89bc 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Connexion USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications qui sollicitent la pile"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sollicite la pile"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications sollicitent la pile"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Touchez pour afficher des détails sur l\'utilisation de la pile et des données"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9c9cdd1..1992068 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Connexion USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications utilisant la batterie"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise la batterie"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications utilisent la batterie"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Appuyer pour obtenir des informations sur l\'utilisation de la batterie et des données"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7dd89aa..7583502 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"conexión USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Estase executando a aplicación"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacións que consomen batería"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo batería"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacións están consumindo batería"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para obter información sobre o uso de datos e a batería"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6ace38d..6f140c4 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB કનેક્શન"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ઍપ ચાલી રહ્યું છે"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ઍપ બૅટરીનો વપરાશ કરી રહ્યાં છે"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> બૅટરીનો ઉપયોગ કરી રહ્યું છે"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ઍપ બૅટરીનો ઉપયોગ કરી રહ્યાં છે"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"બૅટરી અને ડેટા વપરાશ વિશેની વિગતો માટે ટૅપ કરો"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f1e5780..5892dab 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB कनेक्शन"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ऐप अभी इस्तेमाल हो रहा है"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"बैटरी की खपत करने वाले ऐप"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बैटरी का इस्तेमाल कर रहा है"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ऐप बैटरी का इस्तेमाल कर रहे हैं"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बैटरी और डेटा खर्च की जानकारी के लिए छूएं"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 4ff37c5..f4d9842 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Izvodi se aplikacija"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije troše bateriju"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi bateriju"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Broj aplikacija koje koriste bateriju: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podatkovnom prometu"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d01373f..bff1773 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-kapcsolat"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Jelenleg futó alkalmazás"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkumulátort használó alkalmazások"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás használja az akkumulátort"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> alkalmazás használja az akkumulátort"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Koppintson az akkumulátor- és adathasználat részleteinek megtekintéséhez"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a85ed70..1f2e642 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB կապակցում"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Հավելվածն աշխատում է"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Մարտկոցի լիցքը ծախսող հավելվածներ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"«<xliff:g id="APP_NAME">%1$s</xliff:g>» հավելվածը ծախսում է մարտկոցի լիցքը"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> հավելված ծախսում է մարտկոցի լիցքը"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Հպեք՝ մարտկոցի և թրաֆիկի մանրամասները տեսնելու համար"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index cbcb131..4f26eb6 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Sambungan USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikasi berjalan"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikasi yang menggunakan baterai"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan baterai"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikasi sedang meggunakan baterai"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketuk untuk melihat detail penggunaan baterai dan data"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 5d19122..d5539a3 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-tenging"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Forrit er í gangi"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Forrit sem nota rafhlöðuorku"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> notar rafhlöðuorku"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> forrit nota rafhlöðuorku"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index bf26d99..152cfee 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Connessione USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App in esecuzione"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"App che consumano la batteria"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ingrandimento"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> sta consumando la batteria"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> app stanno consumando la batteria"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocca per conoscere i dettagli sull\'utilizzo dei dati e della batteria"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novità: Ingrandimento finestra"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Puoi ingrandire lo schermo in parte o per intero"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Attiva nelle Impostazioni"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Ignora"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 422d810..5ae9436 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"‏חיבור USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"אפליקציה פועלת"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"אפליקציות שמרוקנות את הסוללה"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> משתמשת בסוללה"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> אפליקציות משתמשות בסוללה"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"הקש לקבלת פרטים על צריכה של נתונים וסוללה"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f5adb59..7a54a15 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB 接続"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"実行中のアプリ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"電池を消費しているアプリ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」が電池を使用しています"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個のアプリが電池を使用しています"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"タップして電池やデータの使用量を確認"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 0c3e7b9..1653cb9 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB კავშირი"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"აპი გაშვებულია"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ბატარეის მხარჯავი აპები"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"გადიდება"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> იყენებს ბატარეას"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"ბატარეას <xliff:g id="NUMBER">%1$d</xliff:g> აპი იყენებს"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"შეეხეთ ბატარეისა და მონაცემების მოხმარების შესახებ დეტალური ინფორმაციისთვის"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"სიახლე: ფანჯრის გამადიდებელი"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"ახლა შეგიძლიათ, გაადიდოთ ეკრანი ან მისი ნაწილი"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ჩართვა პარამეტრებში"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"უარყოფა"</string>
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3139dce..635fed7 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB байланысы"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Қолданба қосулы"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Батареяны пайдаланып жатқан қолданбалар"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батареяны пайдалануда"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> қолданба батареяны пайдалануда"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батарея мен деректер трафигі туралы білу үшін түртіңіз"</string>
@@ -319,7 +321,7 @@
     <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">"Explore by Touch функциясын қосу"</string>
+    <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Түртілген элементтерді дыбыстау функциясын қосу"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Түртілген элементтер дауыстап айтылады және экранды қимылдар арқылы зерттеуге болады."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Терілген мәтінді тексеру"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Несиелік карта нөмірі және құпия сөздер сияқты жеке деректі қоса."</string>
@@ -994,9 +996,9 @@
     <string name="searchview_description_clear" msgid="1989371719192982900">"Сұрақты өшіру"</string>
     <string name="searchview_description_submit" msgid="6771060386117334686">"Сұрақ жіберу"</string>
     <string name="searchview_description_voice" msgid="42360159504884679">"Дауыс арқылы іздеу"</string>
-    <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Explore by Touch функциясы қосылсын ба?"</string>
-    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Explore by Touch мүмкіндігін қосқысы келеді. Explore by Touch мүмкіндігі қосылған кезде, саусағыңыздың астындағы нәрсенің сипаттамаларын естисіз не көресіз немесе планшетпен өзара байланысу үшін қимылдайсыз."</string>
-    <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Explore by Touch мүмкіндігін қосқысы келеді. Explore by Touch мүмкіндігі қосылған кезде, саусағыңыздың астындағы нәрсенің сипаттамаларын естисіз не көресіз немесе телефонмен өзара байланысу үшін қимылдайсыз."</string>
+    <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Түртілген элементтерді дыбыстау функциясы қосылсын ба?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Түртілген элементтерді дыбыстау функциясын қосуға рұқсат сұрап тұр. Ол қосылған кезде, саусағыңыздың астындағы элементтің сипаттамасын естіп не көріп тұрасыз немесе планшетті қимылмен басқарасыз."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Түртілген элементтерді дыбыстау функциясын қосуға рұқсат сұрап тұр. Ол қосылған кезде, саусағыңыздың астындағы элементтің сипаттамасын естіп не көріп тұрасыз немесе телефонды қимылмен басқарасыз."</string>
     <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 ай бұрын"</string>
     <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Осыған дейін 1 ай бұрын"</string>
     <plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1316,7 +1318,7 @@
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Аналогтық аудиожабдық анықталды"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Жалғанған құрылғы бұл телефонмен үйлесімсіз. Қосымша ақпарат алу үшін түртіңіз."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"USB арқылы түзету қосылған"</string>
-    <string name="adb_active_notification_message" msgid="5617264033476778211">"USB арқылы түзетуді өшіру үшін түртіңіз"</string>
+    <string name="adb_active_notification_message" msgid="5617264033476778211">"USB арқылы түзетуді өшіру үшін түртіңіз."</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB арқылы түзетуді өшіру үшін таңдаңыз."</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Сымсыз түзету байланыстырылды"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Сымсыз түзетуді өшіру үшін түртіңіз."</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index cc7c72a..1160a40 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"ការ​តភ្ជាប់ USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"កម្មវិធី​ដែល​កំពុង​ដំណើរការ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"កម្មវិធីដែល​កំពុងប្រើថ្ម"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងប្រើថ្ម"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"កម្មវិធីចំនួន <xliff:g id="NUMBER">%1$d</xliff:g> កំពុងប្រើថ្ម"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ចុចដើម្បីមើលព័ត៌មានលម្អិតអំពីការប្រើប្រាស់ទិន្នន័យ និងថ្ម"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index d3a3148..8eb3467 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB ಸಂಪರ್ಕ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App ರನ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಬ್ಯಾಟರಿಯನ್ನು ಉಪಯೋಗಿಸುತ್ತಿವೆ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್, ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಬ್ಯಾಟರಿ ಬಳಸುತ್ತಿವೆ"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ಬ್ಯಾಟರಿ,ಡೇಟಾ ಬಳಕೆಯ ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f08ed6c..46b4a1b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB 연결"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"실행 중인 앱"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"배터리를 소모하는 앱"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 배터리 사용 중"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"앱 <xliff:g id="NUMBER">%1$d</xliff:g>개에서 배터리 사용 중"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"탭하여 배터리 및 데이터 사용량 확인"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index af98023..bb005f3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB аркылуу туташуу"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Колдонмо иштеп жатат"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Колдонмолор батареяңызды коротууда"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу батареяны пайдаланып жатат"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> колдонмо батареяны пайдаланып жатат"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батареянын кубаты жана трафиктин көлөмү жөнүндө билүү үчүн таптап коюңуз"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 35d2e2d..8e4e467 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"ການເຊື່ອມຕໍ່ USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ແອັບກຳລັງເຮັດວຽກ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ແອັບທີ່ກຳລັງໃຊ້ແບັດເຕີຣີ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ແອັບກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ແຕະເພື່ອເບິ່ງລາຍລະອຽດການນຳໃຊ້ແບັດເຕີຣີ ແລະ ອິນເຕີເນັດ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 99e8b1f..a922716 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB jungtis"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programa paleista"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programos, naudojančios akumuliatoriaus energiją"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ naudoja akumuliatoriaus energiją"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programų, naudojančių akumuliatoriaus energiją: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Palieskite ir sužinokite išsamios informacijos apie akumuliatoriaus bei duomenų naudojimą"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 95752c3..2fec712 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB savienojums"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Lietotne darbojas"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Lietotnes, kas patērē akumulatora jaudu"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> izmanto akumulatoru"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> lietotne(-es) izmanto akumulatoru"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pieskarieties, lai skatītu detalizētu informāciju par akumulatora un datu lietojumu"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 5e62762..3d02757 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -155,19 +155,19 @@
     <string name="fcError" msgid="5325116502080221346">"Проблем со поврзувањето или неважечки код за карактеристиката."</string>
     <string name="httpErrorOk" msgid="6206751415788256357">"Во ред"</string>
     <string name="httpError" msgid="3406003584150566720">"Настана грешка на мрежа."</string>
-    <string name="httpErrorLookup" msgid="3099834738227549349">"Не можеше да се најде URL."</string>
+    <string name="httpErrorLookup" msgid="3099834738227549349">"Не може да се најде URL."</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Шемата за автентикација на локацијата не е поддржана."</string>
-    <string name="httpErrorAuth" msgid="469553140922938968">"Не можеше да се автентицира."</string>
+    <string name="httpErrorAuth" msgid="469553140922938968">"Не може да се автентицира."</string>
     <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Автентикацијата преку прокси серверот беше неуспешна."</string>
-    <string name="httpErrorConnect" msgid="3295081579893205617">"Не можеше да се поврзе со серверот."</string>
-    <string name="httpErrorIO" msgid="3860318696166314490">"Не можеше да се комуницира со серверот. Обидете се повторно подоцна."</string>
+    <string name="httpErrorConnect" msgid="3295081579893205617">"Не може да се поврзе со серверот."</string>
+    <string name="httpErrorIO" msgid="3860318696166314490">"Не може да се комуницира со серверот. Обидете се повторно подоцна."</string>
     <string name="httpErrorTimeout" msgid="7446272815190334204">"Времето за поврзување до серверот истече."</string>
     <string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Страницата содржи премногу пренасочувања од серверот."</string>
     <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Протоколот не е поддржан."</string>
-    <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Не можеше да се воспостави безбедна врска."</string>
-    <string name="httpErrorBadUrl" msgid="754447723314832538">"Страницата не можеше да се отвори, бидејќи URL е неважечки."</string>
-    <string name="httpErrorFile" msgid="3400658466057744084">"Не можеше да се пристапи до датотеката."</string>
-    <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Не можеше да се најде бараната датотека."</string>
+    <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Не може да се воспостави безбедна врска."</string>
+    <string name="httpErrorBadUrl" msgid="754447723314832538">"Страницата не може да се отвори, бидејќи URL е неважечки."</string>
+    <string name="httpErrorFile" msgid="3400658466057744084">"Не може да се пристапи до датотеката."</string>
+    <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Не може да се најде бараната датотека."</string>
     <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Се обработуваат премногу барања. Обидете се повторно подоцна."</string>
     <string name="notification_title" msgid="5783748077084481121">"Грешка при пријавување за <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
     <string name="contentServiceSync" msgid="2341041749565687871">"Синхронизирај"</string>
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-врска"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апликацијата работи"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликации што ја трошат батеријата"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерија"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апликации користат батерија"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Допрете за детали за батеријата и потрошениот сообраќај"</string>
@@ -550,7 +552,7 @@
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не е поставен PIN, шема или лозинка"</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_insufficient" msgid="2545149524031515411">"Отпечатокот не може да се обработи. Обидете се повторно."</string>
     <string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
     <string name="fingerprint_acquired_too_fast" msgid="5151661932298844352">"Прстот се движеше пребрзо. Обидете се повторно."</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
@@ -1405,7 +1407,7 @@
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"прашај дали да се игнорираат оптимизациите на батеријата"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Допрете двапати за контрола на зумот"</string>
-    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не можеше да се додаде виџет."</string>
+    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не може да се додаде виџет."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Оди"</string>
     <string name="ime_action_search" msgid="4501435960587287668">"Пребарај"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"Испрати"</string>
@@ -1440,7 +1442,7 @@
     <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Поврзување со секогаш вклучена VPN..."</string>
     <string name="vpn_lockdown_connected" msgid="2853127976590658469">"Поврзани со секогаш вклучена VPN"</string>
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Исклучено од секогаш вклучената VPN"</string>
-    <string name="vpn_lockdown_error" msgid="4453048646854247947">"Не можеше да се поврзе на секогаш вклучената VPN"</string>
+    <string name="vpn_lockdown_error" msgid="4453048646854247947">"Не може да се поврзе на секогаш вклучената VPN"</string>
     <string name="vpn_lockdown_config" msgid="8331697329868252169">"Променете ја мрежата или поставките за VPN"</string>
     <string name="upload_file" msgid="8651942222301634271">"Избери датотека"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"Не е избрана датотека"</string>
@@ -1982,9 +1984,9 @@
     <string name="popup_window_default_title" msgid="6907717596694826919">"Појавен прозорец"</string>
     <string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Верзијата на апликацијата е постара или не е компатибилна со кратенкава"</string>
-    <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Не можеше да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string>
-    <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Не можеше да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string>
-    <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Не можеше да се врати кратенката"</string>
+    <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Не може да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string>
+    <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Не може да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string>
+    <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Не може да се врати кратенката"</string>
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Кратенката е оневозможена"</string>
     <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ДЕИНСТАЛИРАЈ"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"СЕПАК ОТВОРИ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 85a7975..583c46e 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB കണക്ഷൻ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ആപ്പ് പ്രവർത്തിക്കുന്നു"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ബാറ്ററി, ഡാറ്റ ഉപയോഗം എന്നിവയുടെ വിശദാംശങ്ങളറിയാൻ ടാപ്പുചെയ്യുക"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 6523784..081e170 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB холболт"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апп ажиллаж байна"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апп батарей ашиглаж байна"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батерей ашиглаж байна"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апп батерей ашиглаж байна"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батерей, дата ашиглалтын талаар дэлгэрэнгүйг харахын тулд товшино уу"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index e39d46d..0588633 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB कनेक्‍शन"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP चालत आहे"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"बॅटरी लवकर संपवणारी अ‍ॅप्स"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बॅटरी वापरत आहे"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> अ‍ॅप्स बॅटरी वापरत आहेत"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बॅटरी आणि डेटा वापराच्‍या तपशीलांसाठी टॅप करा"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index b38dec2..1d70f76 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Sambungan USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Apl berjalan"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apl yang menggunakan bateri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan bateri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apl sedang menggunakan bateri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index e1f3dcf..097c6c5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB ချိတ်ဆက်မှု"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP လုပ်ဆောင်နေသည်"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"အက်ပ်များက ဘက်ထရီကုန်စေသည်"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> က ဘက်ထရီကို အသုံးပြုနေသည်"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"အက်ပ် <xliff:g id="NUMBER">%1$d</xliff:g> ခုက ဘက်ထရီကို အသုံးပြုနေသည်"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ဘက်ထရီနှင့် ဒေတာအသုံးပြုမှု အသေးစိတ်ကို ကြည့်ရန် တို့ပါ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 1c2e5c0..6fa80dd 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-tilkobling"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App kjører"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apper bruker batteri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruker batteri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apper bruker batteri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trykk for detaljer om batteri- og databruk"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 915e158..28c3525 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -142,7 +142,7 @@
     <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
     <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"WiFi कलिङ"</string>
     <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
-    <string name="wifi_calling_off_summary" msgid="5626710010766902560">"निष्क्रिय"</string>
+    <string name="wifi_calling_off_summary" msgid="5626710010766902560">"अफ"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi मार्फत कल गर्नुहोस्"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"मोबाइल नेटवर्कमार्फत कल गर्नुहोस्"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string>
@@ -203,7 +203,7 @@
     <string name="factory_reset_message" msgid="2657049595153992213">"प्रशासकको एप प्रयोग गर्न मिल्दैन। तपाईंको यन्त्रको डेटा अब मेटाइने छ।\n\nतपाईंसँग प्रश्नहरू भएका खण्डमा आफ्नो संगठनका प्रशासकसँग सम्पर्क गर्नुहोस्।"</string>
     <string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ले छाप्ने कार्यलाई असक्षम पार्यो।"</string>
     <string name="personal_apps_suspension_title" msgid="7561416677884286600">"आफ्नो कार्य प्रोफाइल सक्रिय गर्नुहोस्"</string>
-    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत अनुप्रयोगहरूलाई रोक लगाइन्छ"</string>
+    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत एपहरूलाई रोक लगाइन्छ"</string>
     <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"मिति <xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> बजे व्यक्तिगत एपहरूलाई रोक लगाइने छ। तपाईंका IT एडमिन तपाईंलाई आफ्नो कार्य प्रोफाइल <xliff:g id="NUMBER">%3$d</xliff:g> भन्दा धेरै दिन निष्क्रिय राख्ने अनुमति दिनुहुन्न।"</string>
     <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"सक्रिय गर्नुहोस्"</string>
     <string name="me" msgid="6207584824693813140">"मलाई"</string>
@@ -231,7 +231,7 @@
     <string name="shutdown_confirm" product="default" msgid="136816458966692315">"तपाईँको फोन बन्द हुने छ।"</string>
     <string name="shutdown_confirm_question" msgid="796151167261608447">"के तपाईं बन्द गर्न चाहनुहुन्छ?"</string>
     <string name="reboot_safemode_title" msgid="5853949122655346734">"सुरक्षित मोडमा पुनःबुट गर्नुहोस्"</string>
-    <string name="reboot_safemode_confirm" msgid="1658357874737219624">"सुरक्षित मोडमा तपाईं पुनःबुट गर्न चाहनु हुन्छ? तपाईंले स्थापना गरेका सबै तेस्रो पक्षका अनुप्रयोगहरूलाई असक्षम गराउने छ।"</string>
+    <string name="reboot_safemode_confirm" msgid="1658357874737219624">"सुरक्षित मोडमा तपाईं पुनःबुट गर्न चाहनु हुन्छ? तपाईंले स्थापना गरेका सबै तेस्रो पक्षका एपहरूलाई असक्षम गराउने छ।"</string>
     <string name="recent_tasks_title" msgid="8183172372995396653">"नयाँ"</string>
     <string name="no_recent_tasks" msgid="9063946524312275906">"कुनै नयाँ एपहरू छैनन्।"</string>
     <string name="global_actions" product="tablet" msgid="4412132498517933867">"ट्याब्लेट विकल्पहरू"</string>
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB जडान"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"एप चलिरहेको छ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"एपहरूले ब्याट्री खपत गर्दै छन्"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले ब्याट्री प्रयोग गर्दै छ"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> एपहरूले ब्याट्री प्रयोग गर्दै छन्"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
@@ -366,13 +368,13 @@
     <string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
-    <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"अनुप्रयोगहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्न अनुमति दिनुहोस्।"</string>
-    <string name="permlab_reorderTasks" msgid="7598562301992923804">"चलिरहेका अनुप्रयोगहरूलाई पुनःक्रम गराउनुहोस्"</string>
+    <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"एपहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्न अनुमति दिनुहोस्।"</string>
+    <string name="permlab_reorderTasks" msgid="7598562301992923804">"चलिरहेका एपहरूलाई पुनःक्रम गराउनुहोस्"</string>
     <string name="permdesc_reorderTasks" msgid="8796089937352344183">"कामहरूलाई अग्रभाग र पृष्ठभूमिमा सार्न एपलाई अनुमति दिन्छ। अनुप्रयोगले यो तपाईँको इनपुट बिना नै गर्न सक्छ।"</string>
     <string name="permlab_enableCarMode" msgid="893019409519325311">"कार मोड सक्षम गर्नुहोस्"</string>
     <string name="permdesc_enableCarMode" msgid="56419168820473508">"कार मोडलाई सक्षम पार्न एपलाई अनुमति दिन्छ।"</string>
     <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"एपहरू बन्द गर्नुहोस्"</string>
-    <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य अनुप्रयोगहरूलाई चल्नबाट रोक्न सक्दछ।"</string>
+    <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य एपहरूलाई चल्नबाट रोक्न सक्दछ।"</string>
     <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"यो एप अन्य एपहरूमाथि देखा पर्न सक्छ"</string>
     <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"यो एप अन्य एपहरूमाथि वा स्क्रिनका अन्य भागहरूमा देखा पर्न सक्छ। यसले एपको सामान्य प्रयोगमा अवरोध पुर्याउन सक्छ र अन्य एपहरू देखा पर्ने तरिकालाई परिवर्तन गर्न सक्छ।"</string>
     <string name="permlab_runInBackground" msgid="541863968571682785">"पृष्ठभूमिमा चलाउनुहोस्"</string>
@@ -380,7 +382,7 @@
     <string name="permlab_useDataInBackground" msgid="783415807623038947">"पृष्ठभूमिमा डेटा प्रयोग गर्नुहोस्"</string>
     <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"यो अनुप्रयोगले पृष्ठभूमिमा डेटा प्रयोग गर्नसक्छ। यसले गर्दा धेरै डेटा प्रयोग हुनसक्छ।"</string>
     <string name="permlab_persistentActivity" msgid="464970041740567970">"एपहरू जहिले पनि चल्ने बनाउनुहोस्"</string>
-    <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य अनुप्रयोगहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"</string>
+    <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य एपहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"</string>
     <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android टिभी यन्त्र सुस्त हुन सक्छ।"</string>
     <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"एपलाई मेमोरीमा आफैंको निरन्तरको अंश बनाउन अनुमति दिन्छ। यसले फोनलाई ढिला बनाएर अन्य एपहरूमा मेमोरी SIMित गर्न सक्दछन्।"</string>
     <string name="permlab_foregroundService" msgid="1768855976818467491">"अग्रभूमिको सेवा सञ्चालन गर्नुहोस्"</string>
@@ -398,9 +400,9 @@
     <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android टिभी यन्त्र सुस्त वा अस्थिर हुन सक्छ।"</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"औपचारिक प्रसारणलाई पठाउनको लागि एक एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले फोनलाई ढिलो र अस्थिर बनाउन सक्छ।"</string>
     <string name="permlab_readContacts" msgid="8776395111787429099">"तपाईँका सम्पर्कहरू पढ्नुहोस्"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
-    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
-    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
+    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
+    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
     <string name="permlab_writeContacts" msgid="8919430536404830430">"तपाईँका सम्पर्कहरू परिवर्तन गर्नुहोस्"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
     <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
@@ -621,7 +623,7 @@
     <string name="permlab_readSyncSettings" msgid="6250532864893156277">"समीकरण सेटिङहरू पढ्नुहोस्"</string>
     <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"एपलाई खाताको लागि सिंक सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको एप खातासँग सिंक भएको नभएको निर्धारण गर्न सक्दछ।"</string>
     <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"टगल सिंक खुला र बन्द"</string>
-    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"अनुप्रयोगहरूलाई खाताको लागि सिंक सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ। उदाहरणको लागि, यो खातासँग व्यक्ति एपको सिंक सक्षम गर्न प्रयोग गर्न सकिन्छ।"</string>
+    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"एपहरूलाई खाताको लागि सिंक सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ। उदाहरणको लागि, यो खातासँग व्यक्ति एपको सिंक सक्षम गर्न प्रयोग गर्न सकिन्छ।"</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"सिंक तथ्याङ्कहरू पढ्नुहोस्"</string>
     <string name="permdesc_readSyncStats" msgid="3867809926567379434">"एपलाई खाताको लागि समीकरणको आँकडा समीकरण घटनाहरूको  इतिहास र समीकरण गरिएको डेटाको मापन समेत, पढ्न अनुमति दिन्छ।"</string>
     <string name="permlab_sdcardRead" msgid="5791467020950064920">"आफ्नो आदान प्रदान गरिएको भण्डारणको सामग्रीहरूहरू पढ्नुहोस्"</string>
@@ -649,9 +651,9 @@
     <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"नेटवर्क उपयोग लेखालाई परिमार्जन गर्नुहोस्"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"एपलाई कसरी अनुप्रयोगहरूको विरूद्धमा कसरी नेटवर्क उपयोगी अकाउन्टेड छ भनेर परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूद्वारा प्रयोगको लागि होइन।"</string>
     <string name="permlab_accessNotifications" msgid="7130360248191984741">"सूचनाहरू पहुँच गर्नुहोस्"</string>
-    <string name="permdesc_accessNotifications" msgid="761730149268789668">"अन्य अनुप्रयोगहरूबाट पोस्ट गरिएकासहित पुनःप्राप्त गर्न, परीक्षण गर्न र सूचनाहरू हटाउन अनुप्रयोगहरूलाई अनुमति दिन्छ।"</string>
+    <string name="permdesc_accessNotifications" msgid="761730149268789668">"अन्य अनुप्रयोगहरूबाट पोस्ट गरिएकासहित पुनःप्राप्त गर्न, परीक्षण गर्न र सूचनाहरू हटाउन एपहरूलाई अनुमति दिन्छ।"</string>
     <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"जानकारी श्रोता सेवामा बाँध्नुहोस्"</string>
-    <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"होल्डरलाई सूचना श्रोता सेवाको शीर्ष-स्तरको इन्टरफेस बाँध्न अनुमति दिन्छ। सामान्य अनुप्रयोगहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string>
+    <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"होल्डरलाई सूचना श्रोता सेवाको शीर्ष-स्तरको इन्टरफेस बाँध्न अनुमति दिन्छ। सामान्य एपहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string>
     <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"सर्त प्रदायक सेवामा जोड्न"</string>
     <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"सर्त प्रदायक सेवाको माथिल्लो स्तरको इन्टरफेसमा जोड्न बाहकलाई अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"सपना सेवामा बाँध्नुहोस्"</string>
@@ -675,7 +677,7 @@
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"बाधा नपुर्याउँनुहोस् पहुँच गर्नुहोस्"</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="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियमहरू मिलाउनुहोस्"</string>
     <string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।"</string>
     <string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string>
@@ -1126,7 +1128,7 @@
     <string name="dialog_alert_title" msgid="651856561974090712">"सावधानी"</string>
     <string name="loading" msgid="3138021523725055037">"लोड हुँदै..."</string>
     <string name="capital_on" msgid="2770685323900821829">"चालु"</string>
-    <string name="capital_off" msgid="7443704171014626777">"बन्द"</string>
+    <string name="capital_off" msgid="7443704171014626777">"अफ"</string>
     <string name="checked" msgid="9179896827054513119">"जाँच गरिएको"</string>
     <string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
     <string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
@@ -1879,7 +1881,7 @@
     <string name="default_notification_channel_label" msgid="3697928973567217330">"वर्गीकरण नगरिएको"</string>
     <string name="importance_from_user" msgid="2782756722448800447">"तपाईंले यी सूचनाहरूको महत्त्व सेट गर्नुहोस् ।"</string>
     <string name="importance_from_person" msgid="4235804979664465383">"यसमा सङ्लग्न भएका मानिसहरूको कारणले गर्दा यो महत्वपूर्ण छ।"</string>
-    <string name="notification_history_title_placeholder" msgid="7748630986182249599">"अनुप्रयोगसम्बन्धी आफ्नो रोजाइअनुसारको सूचना"</string>
+    <string name="notification_history_title_placeholder" msgid="7748630986182249599">"एपसम्बन्धी आफ्नो रोजाइअनुसारको सूचना"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (यस खाताको प्रयोगकर्ता पहिले नै अवस्थित छ) मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string>
     <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string>
     <string name="language_selection_title" msgid="52674936078683285">"भाषा थप्नुहोस्"</string>
@@ -1911,7 +1913,7 @@
     <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> लाई पिन गर्नुहोस्"</string>
     <string name="unpin_target" msgid="3963318576590204447">"अनपिन गर्नुहोस्"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> लाई अनपिन गर्नुहोस्"</string>
-    <string name="app_info" msgid="6113278084877079851">"अनुप्रयोगका बारे जानकारी"</string>
+    <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>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f768c83..8b571e8 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App actief"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps die de batterij gebruiken"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruikt de batterij"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps gebruiken de batterij"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tik voor batterij- en datagebruik"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 5206732..4b5c9b0 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB ସଂଯୋଗ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ଆପ୍‍ ଚାଲୁଛି"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ଆପ୍‍ଗୁଡ଼ିକ ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କରିଥା\'ନ୍ତି"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛି"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>ଟି ଆପ୍‍ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 11559f3..5de0885 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB ਕਨੈਕਸ਼ਨ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ਚੱਲ ਰਹੀ ਐਪ"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ਬੈਟਰੀ ਦੀ ਖਪਤ ਕਰਨ ਵਾਲੀਆਂ ਐਪਾਂ"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ਐਪਾਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ਬੈਟਰੀ ਅਤੇ ਡਾਟਾ ਵਰਤੋਂ ਸਬੰਧੀ ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 07773da..427beb5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -186,10 +186,10 @@
       <item quantity="one">Urząd certyfikacji został zainstalowany</item>
     </plurals>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Przez nieznany podmiot zewnętrzny"</string>
-    <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Przez administratora Twojego profilu do pracy"</string>
+    <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Przez administratora Twojego profilu służbowego"</string>
     <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Przez <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
     <string name="work_profile_deleted" msgid="5891181538182009328">"Usunięto profil służbowy"</string>
-    <string name="work_profile_deleted_details" msgid="3773706828364418016">"Brakuje aplikacji administratora profilu do pracy lub jest ona uszkodzona. Dlatego Twój profil służbowy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string>
+    <string name="work_profile_deleted_details" msgid="3773706828364418016">"Brakuje aplikacji administratora profilu służbowego lub jest ona uszkodzona. Dlatego Twój profil służbowy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Twój profil służbowy nie jest już dostępny na tym urządzeniu"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zbyt wiele prób podania hasła"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator odstąpił urządzenie do użytku osobistego"</string>
@@ -206,8 +206,8 @@
     <string name="factory_reset_warning" msgid="6858705527798047809">"Twoje urządzenie zostanie wyczyszczone"</string>
     <string name="factory_reset_message" msgid="2657049595153992213">"Nie można użyć aplikacji administratora. Dane z urządzenia zostaną wykasowane.\n\nJeśli masz pytania, skontaktuj się z administratorem organizacji."</string>
     <string name="printing_disabled_by" msgid="3517499806528864633">"Drukowanie wyłączone przez: <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
-    <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Włącz profil do pracy"</string>
-    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Zablokowano aplikacje osobiste do czasu włączenia profilu do pracy"</string>
+    <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Włącz profil służbowy"</string>
+    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Zablokowano aplikacje osobiste do czasu włączenia profilu służbowego"</string>
     <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Aplikacje osobiste zostaną zablokowane <xliff:g id="DATE">%1$s</xliff:g> o <xliff:g id="TIME">%2$s</xliff:g>. Administrator IT nie pozwala na wyłączenie profilu służbowego na dłużej niż <xliff:g id="NUMBER">%3$d</xliff:g> dni."</string>
     <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Włącz"</string>
     <string name="me" msgid="6207584824693813140">"Ja"</string>
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Połączenie USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Działa aplikacja"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacje zużywające baterię"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> zużywa baterię"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Liczba aplikacji zużywających baterię: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i użycia danych"</string>
@@ -716,7 +718,7 @@
   <string-array name="phoneTypes">
     <item msgid="8996339953292723951">"Dom"</item>
     <item msgid="7740243458912727194">"Komórka"</item>
-    <item msgid="8526146065496663766">"Praca"</item>
+    <item msgid="8526146065496663766">"Służbowy"</item>
     <item msgid="8150904584178569699">"Faks w pracy"</item>
     <item msgid="4537253139152229577">"Faks domowy"</item>
     <item msgid="6751245029698664340">"Pager"</item>
@@ -725,24 +727,24 @@
   </string-array>
   <string-array name="emailAddressTypes">
     <item msgid="7786349763648997741">"Dom"</item>
-    <item msgid="435564470865989199">"Praca"</item>
+    <item msgid="435564470865989199">"Służbowy"</item>
     <item msgid="4199433197875490373">"Inne"</item>
     <item msgid="3233938986670468328">"Niestandardowy"</item>
   </string-array>
   <string-array name="postalAddressTypes">
     <item msgid="3861463339764243038">"Dom"</item>
-    <item msgid="5472578890164979109">"Praca"</item>
+    <item msgid="5472578890164979109">"Służbowy"</item>
     <item msgid="5718921296646594739">"Inny"</item>
     <item msgid="5523122236731783179">"Niestandardowy"</item>
   </string-array>
   <string-array name="imAddressTypes">
     <item msgid="588088543406993772">"Dom"</item>
-    <item msgid="5503060422020476757">"Praca"</item>
+    <item msgid="5503060422020476757">"Służbowy"</item>
     <item msgid="2530391194653760297">"Inne"</item>
     <item msgid="7640927178025203330">"Niestandardowy"</item>
   </string-array>
   <string-array name="organizationTypes">
-    <item msgid="6144047813304847762">"Praca"</item>
+    <item msgid="6144047813304847762">"Służbowy"</item>
     <item msgid="7402720230065674193">"Inne"</item>
     <item msgid="808230403067569648">"Niestandardowy"</item>
   </string-array>
@@ -1462,8 +1464,8 @@
     <string name="deny" msgid="6632259981847676572">"Odmów"</string>
     <string name="permission_request_notification_title" msgid="1810025922441048273">"Prośba o pozwolenie"</string>
     <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Prośba o pozwolenie\ndotyczące konta <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
-    <string name="forward_intent_to_owner" msgid="4620359037192871015">"Używasz tej aplikacji poza profilem do pracy"</string>
-    <string name="forward_intent_to_work" msgid="3620262405636021151">"Używasz tej aplikacji w swoim profilu do pracy"</string>
+    <string name="forward_intent_to_owner" msgid="4620359037192871015">"Używasz tej aplikacji poza profilem służbowym"</string>
+    <string name="forward_intent_to_work" msgid="3620262405636021151">"Używasz tej aplikacji w swoim profilu służbowym"</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"Sposób wprowadzania tekstu"</string>
     <string name="sync_binding_label" msgid="469249309424662147">"Synchronizacja"</string>
     <string name="accessibility_binding_label" msgid="1974602776545801715">"Ułatwienia dostępu"</string>
@@ -1597,7 +1599,7 @@
     <string name="SetupCallDefault" msgid="5581740063237175247">"Odebrać połączenie?"</string>
     <string name="activity_resolver_use_always" msgid="5575222334666843269">"Zawsze"</string>
     <string name="activity_resolver_use_once" msgid="948462794469672658">"Tylko raz"</string>
-    <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s nie obsługuje profilu do pracy"</string>
+    <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s nie obsługuje profilu służbowego"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Tablet"</string>
     <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Telewizor"</string>
     <string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
@@ -1834,7 +1836,7 @@
     <string name="select_day" msgid="2060371240117403147">"Wybierz miesiąc i dzień"</string>
     <string name="select_year" msgid="1868350712095595393">"Wybierz rok"</string>
     <string name="deleted_key" msgid="9130083334943364001">"<xliff:g id="KEY">%1$s</xliff:g> usunięte"</string>
-    <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> (praca)"</string>
+    <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> (służbowy)"</string>
     <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 2"</string>
     <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 3"</string>
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Podaj PIN, aby odpiąć"</string>
@@ -1957,8 +1959,8 @@
     <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> nie jest teraz dostępna. Zarządza tym aplikacja <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
     <string name="app_suspended_more_details" msgid="211260942831587014">"Więcej informacji"</string>
     <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Wznów działanie aplikacji"</string>
-    <string name="work_mode_off_title" msgid="5503291976647976560">"Włączyć profil do pracy?"</string>
-    <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacje do pracy, powiadomienia, dane i inne funkcje profilu do pracy zostaną włączone"</string>
+    <string name="work_mode_off_title" msgid="5503291976647976560">"Włączyć profil służbowy?"</string>
+    <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacje służbowe, powiadomienia, dane i inne funkcje profilu służbowego zostaną włączone"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bfd32f3..3fed028 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Conexão USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App em execução"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão consumindo a bateria"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo a bateria"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão consumindo a bateria"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novo: Lupa de janela"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Agora você pode ampliar uma ou todas as suas telas"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Configurações"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dispensar"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index e83d557..1c890dd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novidade: ampliador da janela"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Já pode ampliar o ecrã parcial ou totalmente."</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Definições"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Ignorar"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bfd32f3..3fed028 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -287,6 +287,7 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Conexão USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App em execução"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão consumindo a bateria"</string>
+    <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo a bateria"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão consumindo a bateria"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string>
@@ -2188,4 +2189,8 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novo: Lupa de janela"</string>
+    <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Agora você pode ampliar uma ou todas as suas telas"</string>
+    <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Configurações"</string>
+    <string name="dismiss_action" msgid="1728820550388704784">"Dispensar"</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index fcd70eb..5cf3076 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Conexiune USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicația rulează"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicațiile consumă bateria"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> folosește bateria"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații folosesc bateria"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 35bf873..efd3c6c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-подключение"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложение активно"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, расходующие заряд"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" расходует заряд"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Несколько приложений (<xliff:g id="NUMBER">%1$d</xliff:g>) расходуют заряд"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Нажмите, чтобы проверить энергопотребление и трафик"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 70cfaa7..100aed1 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB සම්බන්ධතාවය"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"යෙදුම ධාවනය කරමින්"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"බැටරිය භාවිත කරන යෙදුම්"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> බැටරිය භාවිත කරයි"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"යෙදුම් <xliff:g id="NUMBER">%1$d</xliff:g>ක් බැටරිය භාවිත කරයි"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"බැටරි හා දත්ත භාවිතය පිළිබඳව විස්තර සඳහා තට්ටු කරන්න"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cef059d..f7756e5 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Pripojenie USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikácia je spustená"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikácie spotrebúvajúce batériu"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> používa batériu"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikácie (<xliff:g id="NUMBER">%1$d</xliff:g>) používajú batériu"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o batérii a spotrebe dát"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b29748c..df7e0ee 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Povezava USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacija se izvaja"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije, ki porabljajo energijo baterije"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> porablja energijo baterije"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Toliko aplikacij porablja energijo baterije: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dotaknite se za prikaz podrobnosti porabe baterije in prenosa podatkov"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index c852d27..8de3f8c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Lidhja USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacioni është në ekzekutim"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacionet që konsumojnë baterinë"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> po përdor baterinë"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikacione po përdorin baterinë"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trokit për detaje mbi baterinë dhe përdorimin e të dhënave"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6a66c1b..e403118 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -123,28 +123,28 @@
     <string name="roamingText11" msgid="5245687407203281407">"Банер роминга је укључен"</string>
     <string name="roamingText12" msgid="673537506362152640">"Банер роминга је искључен"</string>
     <string name="roamingTextSearching" msgid="5323235489657753486">"Претраживање услуге"</string>
-    <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-ја није успело"</string>
+    <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-а није успело"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-ја. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-а, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-а. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"Проблем у вези са регистровањем позивања преко Wi‑Fi-ја код мобилног оператера: <xliff:g id="CODE">%1$s</xliff:g>"</item>
   </string-array>
     <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
     <skip />
-    <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-ја"</string>
-    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-ја"</string>
+    <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-а"</string>
+    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-а"</string>
     <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN позив"</string>
     <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN позив"</string>
     <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
-    <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-ја | <xliff:g id="SPN">%s</xliff:g>"</string>
+    <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-а | <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
-    <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-ја"</string>
+    <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-а"</string>
     <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string>
-    <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-ја"</string>
+    <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-а"</string>
     <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
     <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Искључено"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-ја"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-а"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Позив преко мобилне мреже"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string>
     <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
@@ -290,6 +290,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB веза"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Активна апликација"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликације које троше батерију"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерију"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Апликације (<xliff:g id="NUMBER">%1$d</xliff:g>) користе батерију"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Додирните за детаље о батерији и потрошњи података"</string>
@@ -2222,4 +2224,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8c28252..cc21f39 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB-anslutning"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App körs"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Appar som drar batteri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> drar batteri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> appar drar batteri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryck för information om batteri- och dataanvändning"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 1ca8fb8..9478b0b 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Muunganisho wa USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programu inaendelea kutekelezwa"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programu zinazotumia betri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index c2972f1..ebaeadd 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB இணைப்பு"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ஆப்ஸ் இயங்குகிறது"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"பேட்டரியைப் பயன்படுத்தும் ஆப்ஸ்"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகிறது"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகின்றன"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"பேட்டரி மற்றும் டேட்டா உபயோக விவரங்களைக் காண, தட்டவும்"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d959a37..c63d175 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB కనెక్షన్"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"యాప్ అమలవుతోంది"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"బ్యాటరీని ఉపయోగిస్తున్న యాప్‌లు"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> బ్యాటరీని ఉపయోగిస్తోంది"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> యాప్‌లు బ్యాటరీని ఉపయోగిస్తున్నాయి"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"బ్యాటరీ మరియు డేటా వినియోగ వివరాల కోసం నొక్కండి"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 5102f14..b0acb12 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"การเชื่อมต่อ USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"แอปที่ทำงานอยู่"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"แอปหลายแอปกำลังใช้แบตเตอรี่"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังใช้แบตเตอรี่"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"แอป <xliff:g id="NUMBER">%1$d</xliff:g> แอปกำลังใช้แบตเตอรี่"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"แตะเพื่อดูรายละเอียดเกี่ยวกับแบตเตอรี่และปริมาณการใช้อินเทอร์เน็ต"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4112fa5..f04e7e1 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Koneksyon ng USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tumatakbo ang app"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Mga app na kumokonsumo ng baterya"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Gumagamit ng baterya ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Gumagamit ng baterya ang <xliff:g id="NUMBER">%1$d</xliff:g> (na) app"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"I-tap para sa mga detalye tungkol sa paggamit ng baterya at data"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8ee1d30..79cfe19 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB bağlantısı"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uygulama çalışıyor"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Pil kullanan uygulamalar"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> pil kullanıyor"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> uygulama pil kullanıyor"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pil ve veri kullanımı ile ilgili ayrıntılar için dokunun"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 778e4a5..a752010 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -293,6 +293,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"З’єднання USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Працює додаток"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Додатки, що використовують заряд акумулятора"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> використовує заряд акумулятора"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Додатків, що використовують заряд акумулятора: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Торкніться, щоб перевірити використання акумулятора й трафік"</string>
@@ -2256,4 +2258,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 78c9330..4324e36 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"‏USB کنکشن"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ایپ چل رہی ہے"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ایپس بیٹری خرچ کر رہی ہیں"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> بیٹری کا استعمال کر رہی ہے"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ایپس بیٹری کا استعمال کر رہی ہیں"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"بیٹری اور ڈیٹا استعمال کے بارے میں تفصیلات کے لیے تھپتھپائیں"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 683a911..923831c 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB orqali ulanish"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ilova faol"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareya quvvatini sarflayotgan ilovalar"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi batareya quvvatini sarflamoqda"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ta ilova batareya quvvatini sarflamoqda"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya va trafik sarfi tafsilotlari uchun ustiga bosing"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 050fa0b..296261f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Kết nối USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ứng dụng đang chạy"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Các ứng dụng tiêu thụ pin"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang sử dụng pin"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang sử dụng pin"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ec01ae3..a27e7b1 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB 连接"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"应用正在运行中"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"消耗电量的应用"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在消耗电量"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 个应用正在消耗电量"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"点按即可详细了解电量和流量消耗情况"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 58232b9..5099ebf 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB 連線"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式正在執行"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"耗用電量的應用程式"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在使用電量"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在使用電量"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕按即可查看電池和數據用量詳情"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 617a6715..31bd99b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"USB 連線"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式執行中"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"正在耗用電量的應用程式"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在耗用電量"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在耗用電量"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕觸即可查看電池和數據用量詳情"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index fab8996..28eed9b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -287,6 +287,8 @@
     <string name="notification_channel_usb" msgid="1528280969406244896">"Ukuxhumeka kwe-USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uhlelo loksuebenza olusebenzayo"</string>
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Izinhlelo zokusebenza ezidla ibhethri"</string>
+    <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) -->
+    <skip />
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> isebenzisa ibhethri"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> izinhlelo zokusebenza zisebenzisa ibhethri"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Thepha ngemininingwane ekusetshenzisweni kwebhethri nedatha"</string>
@@ -2188,4 +2190,12 @@
     <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
     <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
+    <!-- no translation found for window_magnification_prompt_title (8197528399699536320) -->
+    <skip />
+    <!-- no translation found for window_magnification_prompt_content (4166711383253283838) -->
+    <skip />
+    <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) -->
+    <skip />
+    <!-- no translation found for dismiss_action (1728820550388704784) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fc9f670..c32e8dc 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2834,6 +2834,38 @@
         <attr name="resource" format="reference" />
     </declare-styleable>
 
+    <!-- The <code>property</code> tag is used to attach additional data that can
+         be supplied to the parent component. A component element can contain any
+         number of <code>property</code> subelements. Valid names are any of the
+         <code>PROPERTY_</code> constants defined in the
+         {@link android.content.pm.PackageManager PackageManager} class. Values
+         are obtained using the appropriate method on the
+         {@link android.content.pm.PackageManager.Property PackageManager.Property} class.
+         <p>Ordinary values are specified through the value attribute. Resource IDs are
+         specified through the resource attribute.
+         <p>It is invalid to specify both a value and resource attributes. -->
+    <declare-styleable name="AndroidManifestProperty"
+         parent="AndroidManifestApplication
+                 AndroidManifestActivity
+                 AndroidManifestReceiver
+                 AndroidManifestProvider
+                 AndroidManifestService">
+        <attr name="name" />
+        <!-- Concrete value to assign to this property.
+             The data can later be retrieved from the property object
+             through
+             {@link android.content.pm.PackageManager.Property#getString Property.getString},
+             {@link android.content.pm.PackageManager.Property#getInteger Property.getInteger},
+             {@link android.content.pm.PackageManager.Property#getBoolean Property.getBoolean},
+             or {@link android.content.pm.PackageManager.Property#getFloat Property.getFloat}
+             depending on the type used here. -->
+        <attr name="value" />
+        <!-- The resource identifier to assign to this property.
+             The resource identifier can later be retrieved from the property object through
+             {@link android.content.pm.PackageManager.Property#getResourceId Property.getResourceId}. -->
+        <attr name="resource" />
+    </declare-styleable>
+
     <!-- The <code>intent-filter</code> tag is used to construct an
          {@link android.content.IntentFilter} object that will be used
          to determine which component can handle a particular
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 79eae67..19591f6 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -259,6 +259,9 @@
     <!-- The height of the background for a notification header on a group -->
     <dimen name="notification_header_background_height">49.5dp</dimen>
 
+    <!-- The height of the full-width touch rectangle for the notification header -->
+    <dimen name="notification_header_touchable_height">36dp</dimen>
+
     <!-- The top padding for the notification header -->
     <dimen name="notification_header_padding_top">16dp</dimen>
 
@@ -340,11 +343,8 @@
     <!-- The margin of the content to an image-->
     <dimen name="notification_content_image_margin_end">8dp</dimen>
 
-    <!-- The padding at the end of actions when the bubble button is visible-->
-    <dimen name="bubble_visible_padding_end">3dp</dimen>
-
-    <!-- The padding at the end of actions when the bubble button is gone-->
-    <dimen name="bubble_gone_padding_end">12dp</dimen>
+    <!-- The padding at the end of actions when the snooze and bubble buttons are gone-->
+    <dimen name="snooze_and_bubble_gone_padding_end">12dp</dimen>
 
     <!-- The spacing between messages in Notification.MessagingStyle -->
     <dimen name="notification_messaging_spacing">6dp</dimen>
@@ -744,6 +744,9 @@
     <dimen name="conversation_expand_button_size">80dp</dimen>
     <!-- Top margin of the expand button for conversations when expanded -->
     <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen>
+    <!-- Side margin of the expand button for conversations.
+         width of expand asset (22) + 2 * this (13) == notification_header_expand_icon_size (48) -->
+    <dimen name="conversation_expand_button_side_margin">13dp</dimen>
     <!-- Side margins of the conversation badge in relation to the conversation icon -->
     <dimen name="conversation_badge_side_margin">36dp</dimen>
     <!-- size of the notification badge when applied to the conversation icon -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8eb0853..be6b6b1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -717,6 +717,10 @@
         [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6665375982962336520] -->
     <string name="notification_channel_foreground_service">Apps consuming battery</string>
 
+    <!-- Text shown when viewing channel settings for notifications related to accessibility
+         magnification. [CHAR_LIMIT=NONE]-->
+    <string name="notification_channel_accessibility_magnification">Magnification</string>
+
     <!-- Label for foreground service notification when one app is running.
     [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
     <string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is
@@ -5767,4 +5771,16 @@
     <string name="config_pdp_reject_service_not_subscribed"></string>
     <!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] -->
     <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string>
+
+    <!-- Window magnification prompt related string. -->
+
+    <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
+    <string name="window_magnification_prompt_title">New: Window Magnifier</string>
+    <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
+    <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+    <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
+    <string name="turn_on_magnification_settings_action">Turn on in Settings</string>
+    <!-- Notification action to dismiss. [CHAR LIMIT=50] -->
+    <string name="dismiss_action">Dismiss</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 23733af..40aae9e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2861,6 +2861,7 @@
   <java-symbol type="id" name="header_text" />
   <java-symbol type="id" name="header_text_secondary" />
   <java-symbol type="id" name="expand_button" />
+  <java-symbol type="id" name="alternate_expand_target" />
   <java-symbol type="id" name="notification_header" />
   <java-symbol type="id" name="notification_top_line" />
   <java-symbol type="id" name="time_divider" />
@@ -2878,6 +2879,7 @@
   <java-symbol type="dimen" name="notification_content_margin_top" />
   <java-symbol type="dimen" name="notification_content_margin" />
   <java-symbol type="dimen" name="notification_header_background_height" />
+  <java-symbol type="dimen" name="notification_header_touchable_height" />
   <java-symbol type="dimen" name="notification_header_expand_icon_size" />
   <java-symbol type="dimen" name="notification_expand_button_padding_top" />
   <java-symbol type="dimen" name="notification_header_icon_size" />
@@ -3458,6 +3460,7 @@
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
   <java-symbol type="string" name="notification_channel_system_changes" />
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
+  <java-symbol type="string" name="notification_channel_accessibility_magnification" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
@@ -3559,8 +3562,6 @@
   <java-symbol type="id" name="clip_children_tag" />
   <java-symbol type="id" name="bubble_button" />
   <java-symbol type="id" name="snooze_button" />
-  <java-symbol type="dimen" name="bubble_visible_padding_end" />
-  <java-symbol type="dimen" name="bubble_gone_padding_end" />
   <java-symbol type="dimen" name="text_size_body_2_material" />
   <java-symbol type="dimen" name="messaging_avatar_size" />
   <java-symbol type="dimen" name="messaging_group_sending_progress_size" />
@@ -4096,4 +4097,10 @@
   <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
 
   <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
+
+  <!-- Window magnification prompt -->
+  <java-symbol type="string" name="window_magnification_prompt_title" />
+  <java-symbol type="string" name="window_magnification_prompt_content" />
+  <java-symbol type="string" name="turn_on_magnification_settings_action" />
+  <java-symbol type="string" name="dismiss_action" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 2f2bef8..119b70a 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -24,16 +24,18 @@
 
     @Test
     public void testBuildEmailAndGetValue() {
-        AppSearchEmail email = new AppSearchEmail.Builder("uri")
-                .setFrom("FakeFromAddress")
-                .setCc("CC1", "CC2")
-                // Score and Property are mixed into the middle to make sure DocumentBuilder's
-                // methods can be interleaved with EmailBuilder's methods.
-                .setScore(1)
-                .setPropertyString("propertyKey", "propertyValue1", "propertyValue2")
-                .setSubject("subject")
-                .setBody("EmailBody")
-                .build();
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("uri")
+                        .setFrom("FakeFromAddress")
+                        .setCc("CC1", "CC2")
+                        // Score and Property are mixed into the middle to make sure
+                        // DocumentBuilder's
+                        // methods can be interleaved with EmailBuilder's methods.
+                        .setScore(1)
+                        .setPropertyString("propertyKey", "propertyValue1", "propertyValue2")
+                        .setSubject("subject")
+                        .setBody("EmailBody")
+                        .build();
 
         assertThat(email.getUri()).isEqualTo("uri");
         assertThat(email.getFrom()).isEqualTo("FakeFromAddress");
@@ -42,8 +44,9 @@
         assertThat(email.getBcc()).isNull();
         assertThat(email.getScore()).isEqualTo(1);
         assertThat(email.getPropertyString("propertyKey")).isEqualTo("propertyValue1");
-        assertThat(email.getPropertyStringArray("propertyKey")).asList().containsExactly(
-                "propertyValue1", "propertyValue2");
+        assertThat(email.getPropertyStringArray("propertyKey"))
+                .asList()
+                .containsExactly("propertyValue1", "propertyValue2");
         assertThat(email.getSubject()).isEqualTo("subject");
         assertThat(email.getBody()).isEqualTo("EmailBody");
     }
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
deleted file mode 100644
index c171270..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
+++ /dev/null
@@ -1,74 +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.app.appsearch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.expectThrows;
-
-import android.app.appsearch.AppSearchSchema.PropertyConfig;
-import android.app.appsearch.exceptions.IllegalSchemaException;
-
-import org.junit.Test;
-
-public class AppSearchSchemaTest {
-    @Test
-    public void testInvalidEnums() {
-        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
-        expectThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
-        expectThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
-    }
-
-    @Test
-    public void testMissingFields() {
-        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
-        IllegalSchemaException e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: dataType");
-
-        builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
-        e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: schemaType");
-
-        builder.setSchemaType("TestType");
-        e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: cardinality");
-
-        builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
-        builder.build();
-    }
-
-    @Test
-    public void testDuplicateProperties() {
-        AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email")
-                .addProperty(new PropertyConfig.Builder("subject")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                );
-        IllegalSchemaException e = expectThrows(IllegalSchemaException.class,
-                () -> builder.addProperty(new PropertyConfig.Builder("subject")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()));
-        assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
deleted file mode 100644
index 9c29943..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ /dev/null
@@ -1,252 +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.app.appsearch;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-
-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, (byte) 7};
-    private static final GenericDocument sDocumentProperties1 = new GenericDocument
-            .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
-            .setCreationTimestampMillis(12345L)
-            .build();
-    private static final GenericDocument sDocumentProperties2 = new GenericDocument
-            .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
-            .setCreationTimestampMillis(6789L)
-            .build();
-
-    @Test
-    public void testDocumentEquals_Identical() {
-        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setTtlMillis(1L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .build();
-        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setTtlMillis(1L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .build();
-        assertThat(document1).isEqualTo(document2);
-        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
-    }
-
-    @Test
-    public void testDocumentEquals_DifferentOrder() {
-        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .build();
-
-        // Create second document with same parameter but different order.
-        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .build();
-        assertThat(document1).isEqualTo(document2);
-        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
-    }
-
-    @Test
-    public void testDocumentEquals_Failure() {
-        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .build();
-
-        // Create second document with same order but different value.
-        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyLong("longKey1", 1L, 2L, 4L) // Different
-                .build();
-        assertThat(document1).isNotEqualTo(document2);
-        assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
-    }
-
-    @Test
-    public void testDocumentEquals_Failure_RepeatedFieldOrder() {
-        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .build();
-
-        // Create second document with same order but different value.
-        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyBoolean("booleanKey1", true, true, false) // Different
-                .build();
-        assertThat(document1).isNotEqualTo(document2);
-        assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
-    }
-
-    @Test
-    public void testDocumentGetSingleValue() {
-        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setScore(1)
-                .setTtlMillis(1L)
-                .setPropertyLong("longKey1", 1L)
-                .setPropertyDouble("doubleKey1", 1.0)
-                .setPropertyBoolean("booleanKey1", true)
-                .setPropertyString("stringKey1", "test-value1")
-                .setPropertyBytes("byteKey1", sByteArray1)
-                .setPropertyDocument("documentKey1", sDocumentProperties1)
-                .build();
-        assertThat(document.getUri()).isEqualTo("uri1");
-        assertThat(document.getTtlMillis()).isEqualTo(1L);
-        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
-        assertThat(document.getCreationTimestampMillis()).isEqualTo(5);
-        assertThat(document.getScore()).isEqualTo(1);
-        assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
-        assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0);
-        assertThat(document.getPropertyBoolean("booleanKey1")).isTrue();
-        assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
-        assertThat(document.getPropertyBytes("byteKey1"))
-                .asList().containsExactly((byte) 1, (byte) 2, (byte) 3);
-        assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1);
-    }
-
-    @Test
-    public void testDocumentGetArrayValues() {
-        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .build();
-
-        assertThat(document.getUri()).isEqualTo("uri1");
-        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
-        assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L);
-        assertThat(document.getPropertyDoubleArray("doubleKey1")).usingExactEquality()
-                .containsExactly(1.0, 2.0, 3.0);
-        assertThat(document.getPropertyBooleanArray("booleanKey1")).asList()
-                .containsExactly(true, false, true);
-        assertThat(document.getPropertyStringArray("stringKey1")).asList()
-                .containsExactly("test-value1", "test-value2", "test-value3");
-        assertThat(document.getPropertyBytesArray("byteKey1")).asList()
-                .containsExactly(sByteArray1, sByteArray2);
-        assertThat(document.getPropertyDocumentArray("documentKey1")).asList()
-                .containsExactly(sDocumentProperties1, sDocumentProperties2);
-    }
-
-    @Test
-    public void testDocument_ToString() throws Exception {
-        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "String1", "String2", "String3")
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDocument("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() {
-        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
-                .setScore(1)
-                .setPropertyLong("longKey1", 1L)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .build();
-
-        // Get a value for a key that doesn't exist
-        assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0);
-        assertThat(document.getPropertyDoubleArray("doubleKey1")).isNull();
-
-        // Get a value with a single element as an array and as a single value
-        assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
-        assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L);
-
-        // Get a value with multiple elements as an array and as a single value
-        assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
-        assertThat(document.getPropertyStringArray("stringKey1")).asList()
-                .containsExactly("test-value1", "test-value2", "test-value3");
-
-        // Get a value of the wrong type
-        assertThat(document.getPropertyDouble("longKey1")).isEqualTo(0.0);
-        assertThat(document.getPropertyDoubleArray("longKey1")).isNull();
-    }
-
-    @Test
-    public void testDocumentInvalid() {
-        GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1");
-        expectThrows(
-                IllegalArgumentException.class,
-                () -> builder.setPropertyBoolean("test", new boolean[]{}));
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index d4635fd..9fd480d 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -18,69 +18,33 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.expectThrows;
-
 import android.os.Bundle;
 
 import org.junit.Test;
 
 public class SearchSpecTest {
     @Test
-    public void buildSearchSpecWithoutTermMatchType() {
-        expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
-                .addSchema("testSchemaType")
-                .build());
-    }
-
-    @Test
-    public void testBuildSearchSpec() {
-        SearchSpec searchSpec = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addNamespace("namespace1", "namespace2")
-                .addSchema("schemaTypes1", "schemaTypes2")
-                .setSnippetCount(5)
-                .setSnippetCountPerProperty(10)
-                .setMaxSnippetSize(15)
-                .setNumPerPage(42)
-                .setOrder(SearchSpec.ORDER_ASCENDING)
-                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
-                .build();
-
-        assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
-        assertThat(searchSpec.getNamespaces())
-                .containsExactly("namespace1", "namespace2").inOrder();
-        assertThat(searchSpec.getSchemas())
-                .containsExactly("schemaTypes1", "schemaTypes2").inOrder();
-        assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
-        assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
-        assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
-        assertThat(searchSpec.getNumPerPage()).isEqualTo(42);
-        assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
-        assertThat(searchSpec.getRankingStrategy())
-                .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
-    }
-
-    @Test
     public void testGetBundle() {
-        SearchSpec searchSpec = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addNamespace("namespace1", "namespace2")
-                .addSchema("schemaTypes1", "schemaTypes2")
-                .setSnippetCount(5)
-                .setSnippetCountPerProperty(10)
-                .setMaxSnippetSize(15)
-                .setNumPerPage(42)
-                .setOrder(SearchSpec.ORDER_ASCENDING)
-                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
-                .build();
+        SearchSpec searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                        .addNamespace("namespace1", "namespace2")
+                        .addSchemaType("schemaTypes1", "schemaTypes2")
+                        .setSnippetCount(5)
+                        .setSnippetCountPerProperty(10)
+                        .setMaxSnippetSize(15)
+                        .setResultCountPerPage(42)
+                        .setOrder(SearchSpec.ORDER_ASCENDING)
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+                        .build();
 
         Bundle bundle = searchSpec.getBundle();
         assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD))
                 .isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
-        assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)).containsExactly(
-                "namespace1", "namespace2");
-        assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD)).containsExactly(
-                "schemaTypes1", "schemaTypes2");
+        assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD))
+                .containsExactly("namespace1", "namespace2");
+        assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
+                .containsExactly("schemaTypes1", "schemaTypes2");
         assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5);
         assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10);
         assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15);
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java
new file mode 100644
index 0000000..9c34b17
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.AppSearchResult;
+
+import org.junit.Test;
+
+public class AppSearchResultCtsTest {
+
+    @Test
+    public void testResultEquals_identical() {
+        AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String");
+        AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("String");
+
+        assertThat(result1).isEqualTo(result2);
+        assertThat(result1.hashCode()).isEqualTo(result2.hashCode());
+
+        AppSearchResult<String> result3 =
+                AppSearchResult.newFailedResult(
+                        AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
+        AppSearchResult<String> result4 =
+                AppSearchResult.newFailedResult(
+                        AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
+
+        assertThat(result3).isEqualTo(result4);
+        assertThat(result3.hashCode()).isEqualTo(result4.hashCode());
+    }
+
+    @Test
+    public void testResultEquals_failure() {
+        AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String");
+        AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("Wrong");
+        AppSearchResult<String> resultNull = AppSearchResult.newSuccessfulResult(/*value=*/ null);
+
+        assertThat(result1).isNotEqualTo(result2);
+        assertThat(result1.hashCode()).isNotEqualTo(result2.hashCode());
+        assertThat(result1).isNotEqualTo(resultNull);
+        assertThat(result1.hashCode()).isNotEqualTo(resultNull.hashCode());
+
+        AppSearchResult<String> result3 =
+                AppSearchResult.newFailedResult(
+                        AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
+        AppSearchResult<String> result4 =
+                AppSearchResult.newFailedResult(AppSearchResult.RESULT_IO_ERROR, "errorMessage");
+
+        assertThat(result3).isNotEqualTo(result4);
+        assertThat(result3.hashCode()).isNotEqualTo(result4.hashCode());
+
+        AppSearchResult<String> result5 =
+                AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR, "Wrong");
+
+        assertThat(result3).isNotEqualTo(result5);
+        assertThat(result3.hashCode()).isNotEqualTo(result5.hashCode());
+
+        AppSearchResult<String> result6 =
+                AppSearchResult.newFailedResult(
+                        AppSearchResult.RESULT_INTERNAL_ERROR, /*errorMessage=*/ null);
+
+        assertThat(result3).isNotEqualTo(result6);
+        assertThat(result3.hashCode()).isNotEqualTo(result6.hashCode());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java
new file mode 100644
index 0000000..2eaebd6
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.AppSearchSchema.PropertyConfig;
+import android.app.appsearch.exceptions.IllegalSchemaException;
+
+import org.junit.Test;
+
+public class AppSearchSchemaCtsTest {
+    @Test
+    public void testInvalidEnums() {
+        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
+        expectThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
+        expectThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
+    }
+
+    @Test
+    public void testMissingFields() {
+        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
+        IllegalSchemaException e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: dataType");
+
+        builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: schemaType");
+
+        builder.setSchemaType("TestType");
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: cardinality");
+
+        builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
+        builder.build();
+    }
+
+    @Test
+    public void testDuplicateProperties() {
+        AppSearchSchema.Builder builder =
+                new AppSearchSchema.Builder("Email")
+                        .addProperty(
+                                new PropertyConfig.Builder("subject")
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build());
+        IllegalSchemaException e =
+                expectThrows(
+                        IllegalSchemaException.class,
+                        () ->
+                                builder.addProperty(
+                                        new PropertyConfig.Builder("subject")
+                                                .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                                .setIndexingType(
+                                                        PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                                .setTokenizerType(
+                                                        PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                .build()));
+        assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java
new file mode 100644
index 0000000..657d556
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java
@@ -0,0 +1,286 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.appsearch.GenericDocument;
+
+import org.junit.Test;
+
+public class GenericDocumentCtsTest {
+    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, (byte) 7};
+    private static final GenericDocument sDocumentProperties1 =
+            new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+                    .setCreationTimestampMillis(12345L)
+                    .build();
+    private static final GenericDocument sDocumentProperties2 =
+            new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+                    .setCreationTimestampMillis(6789L)
+                    .build();
+
+    @Test
+    public void testDocumentEquals_identical() {
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setTtlMillis(1L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyDocument(
+                                "documentKey1", sDocumentProperties1, sDocumentProperties2)
+                        .build();
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setTtlMillis(1L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyDocument(
+                                "documentKey1", sDocumentProperties1, sDocumentProperties2)
+                        .build();
+        assertThat(document1).isEqualTo(document2);
+        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
+    }
+
+    @Test
+    public void testDocumentEquals_differentOrder() {
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyDocument(
+                                "documentKey1", sDocumentProperties1, sDocumentProperties2)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .build();
+
+        // Create second document with same parameter but different order.
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyDocument(
+                                "documentKey1", sDocumentProperties1, sDocumentProperties2)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .build();
+        assertThat(document1).isEqualTo(document2);
+        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
+    }
+
+    @Test
+    public void testDocumentEquals_failure() {
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .build();
+
+        // Create second document with same order but different value.
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyLong("longKey1", 1L, 2L, 4L) // Different
+                        .build();
+        assertThat(document1).isNotEqualTo(document2);
+        assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
+    }
+
+    @Test
+    public void testDocumentEquals_repeatedFieldOrder_failure() {
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .build();
+
+        // Create second document with same order but different value.
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyBoolean("booleanKey1", true, true, false) // Different
+                        .build();
+        assertThat(document1).isNotEqualTo(document2);
+        assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
+    }
+
+    @Test
+    public void testDocumentGetSingleValue() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setScore(1)
+                        .setTtlMillis(1L)
+                        .setPropertyLong("longKey1", 1L)
+                        .setPropertyDouble("doubleKey1", 1.0)
+                        .setPropertyBoolean("booleanKey1", true)
+                        .setPropertyString("stringKey1", "test-value1")
+                        .setPropertyBytes("byteKey1", sByteArray1)
+                        .setPropertyDocument("documentKey1", sDocumentProperties1)
+                        .build();
+        assertThat(document.getUri()).isEqualTo("uri1");
+        assertThat(document.getTtlMillis()).isEqualTo(1L);
+        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
+        assertThat(document.getCreationTimestampMillis()).isEqualTo(5);
+        assertThat(document.getScore()).isEqualTo(1);
+        assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
+        assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0);
+        assertThat(document.getPropertyBoolean("booleanKey1")).isTrue();
+        assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
+        assertThat(document.getPropertyBytes("byteKey1"))
+                .asList()
+                .containsExactly((byte) 1, (byte) 2, (byte) 3);
+        assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1);
+    }
+
+    @Test
+    public void testDocumentGetArrayValues() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyDocument(
+                                "documentKey1", sDocumentProperties1, sDocumentProperties2)
+                        .build();
+
+        assertThat(document.getUri()).isEqualTo("uri1");
+        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
+        assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L);
+        assertThat(document.getPropertyDoubleArray("doubleKey1"))
+                .usingExactEquality()
+                .containsExactly(1.0, 2.0, 3.0);
+        assertThat(document.getPropertyBooleanArray("booleanKey1"))
+                .asList()
+                .containsExactly(true, false, true);
+        assertThat(document.getPropertyStringArray("stringKey1"))
+                .asList()
+                .containsExactly("test-value1", "test-value2", "test-value3");
+        assertThat(document.getPropertyBytesArray("byteKey1"))
+                .asList()
+                .containsExactly(sByteArray1, sByteArray2);
+        assertThat(document.getPropertyDocumentArray("documentKey1"))
+                .asList()
+                .containsExactly(sDocumentProperties1, sDocumentProperties2);
+    }
+
+    @Test
+    public void testDocument_toString() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString("stringKey1", "String1", "String2", "String3")
+                        .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
+                        .setPropertyDocument(
+                                "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() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("uri1", "schemaType1")
+                        .setScore(1)
+                        .setPropertyLong("longKey1", 1L)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .build();
+
+        // Get a value for a key that doesn't exist
+        assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0);
+        assertThat(document.getPropertyDoubleArray("doubleKey1")).isNull();
+
+        // Get a value with a single element as an array and as a single value
+        assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
+        assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L);
+
+        // Get a value with multiple elements as an array and as a single value
+        assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
+        assertThat(document.getPropertyStringArray("stringKey1"))
+                .asList()
+                .containsExactly("test-value1", "test-value2", "test-value3");
+
+        // Get a value of the wrong type
+        assertThat(document.getPropertyDouble("longKey1")).isEqualTo(0.0);
+        assertThat(document.getPropertyDoubleArray("longKey1")).isNull();
+    }
+
+    @Test
+    public void testDocumentInvalid() {
+        GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("uri1", "schemaType1");
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> builder.setPropertyBoolean("test", new boolean[] {}));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java
new file mode 100644
index 0000000..50bca27
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.appsearch.SearchSpec;
+
+import org.junit.Test;
+
+public class SearchSpecCtsTest {
+    @Test
+    public void buildSearchSpecWithoutTermMatchType() {
+        RuntimeException e =
+                expectThrows(
+                        RuntimeException.class,
+                        () -> new SearchSpec.Builder().addSchemaType("testSchemaType").build());
+        assertThat(e).hasMessageThat().contains("Missing termMatchType field");
+    }
+
+    @Test
+    public void testBuildSearchSpec() {
+        SearchSpec searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                        .addNamespace("namespace1", "namespace2")
+                        .addSchemaType("schemaTypes1", "schemaTypes2")
+                        .setSnippetCount(5)
+                        .setSnippetCountPerProperty(10)
+                        .setMaxSnippetSize(15)
+                        .setResultCountPerPage(42)
+                        .setOrder(SearchSpec.ORDER_ASCENDING)
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+                        .build();
+
+        assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
+        assertThat(searchSpec.getNamespaces())
+                .containsExactly("namespace1", "namespace2")
+                .inOrder();
+        assertThat(searchSpec.getSchemaTypes())
+                .containsExactly("schemaTypes1", "schemaTypes2")
+                .inOrder();
+        assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
+        assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
+        assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
+        assertThat(searchSpec.getResultCountPerPage()).isEqualTo(42);
+        assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
+        assertThat(searchSpec.getRankingStrategy())
+                .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/customer/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/customer/CustomerDocumentTest.java
new file mode 100644
index 0000000..29b5754
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/customer/CustomerDocumentTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.cts.customer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.NonNull;
+import android.app.appsearch.GenericDocument;
+
+import org.junit.Test;
+
+/**
+ * Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by
+ * developers.
+ *
+ * <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.
+ */
+public class CustomerDocumentTest {
+
+    private static final byte[] BYTE_ARRAY1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
+    private static final byte[] BYTE_ARRAY2 = new byte[] {(byte) 4, (byte) 5, (byte) 6};
+    private static final GenericDocument DOCUMENT_PROPERTIES1 =
+            new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+                    .build();
+    private static final GenericDocument DOCUMENT_PROPERTIES2 =
+            new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+                    .build();
+
+    @Test
+    public void testBuildCustomerDocument() {
+        CustomerDocument customerDocument =
+                new CustomerDocument.Builder("uri1")
+                        .setScore(1)
+                        .setCreationTimestampMillis(0)
+                        .setPropertyLong("longKey1", 1L, 2L, 3L)
+                        .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                        .setPropertyBoolean("booleanKey1", true, false, true)
+                        .setPropertyString(
+                                "stringKey1", "test-value1", "test-value2", "test-value3")
+                        .setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2)
+                        .setPropertyDocument(
+                                "documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2)
+                        .build();
+
+        assertThat(customerDocument.getUri()).isEqualTo("uri1");
+        assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument");
+        assertThat(customerDocument.getScore()).isEqualTo(1);
+        assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L);
+        assertThat(customerDocument.getPropertyLongArray("longKey1"))
+                .asList()
+                .containsExactly(1L, 2L, 3L);
+        assertThat(customerDocument.getPropertyDoubleArray("doubleKey1"))
+                .usingExactEquality()
+                .containsExactly(1.0, 2.0, 3.0);
+        assertThat(customerDocument.getPropertyBooleanArray("booleanKey1"))
+                .asList()
+                .containsExactly(true, false, true);
+        assertThat(customerDocument.getPropertyStringArray("stringKey1"))
+                .asList()
+                .containsExactly("test-value1", "test-value2", "test-value3");
+        assertThat(customerDocument.getPropertyBytesArray("byteKey1"))
+                .asList()
+                .containsExactly(BYTE_ARRAY1, BYTE_ARRAY2);
+        assertThat(customerDocument.getPropertyDocumentArray("documentKey1"))
+                .asList()
+                .containsExactly(DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2);
+    }
+
+    /**
+     * An example document type for test purposes, defined outside of {@link GenericDocument} (the
+     * way an external developer would define it).
+     */
+    private static class CustomerDocument extends GenericDocument {
+        private CustomerDocument(GenericDocument document) {
+            super(document);
+        }
+
+        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/app/appsearch/external/app/customer/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
deleted file mode 100644
index d56d0c3..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
+++ /dev/null
@@ -1,97 +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.app.appsearch.customer;
-
-import android.annotation.NonNull;
-import android.app.appsearch.GenericDocument;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-/**
- * Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by
- * developers.
- *
- * <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.
- */
-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 GenericDocument sDocumentProperties1 = new GenericDocument
-            .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
-            .build();
-    private static GenericDocument sDocumentProperties2 = new GenericDocument
-            .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
-            .build();
-
-    @Test
-    public void testBuildCustomerDocument() {
-        CustomerDocument customerDocument = new CustomerDocument.Builder("uri1")
-                .setScore(1)
-                .setCreationTimestampMillis(0)
-                .setPropertyLong("longKey1", 1L, 2L, 3L)
-                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
-                .setPropertyBoolean("booleanKey1", true, false, true)
-                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
-                .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
-                .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
-                .build();
-
-        assertThat(customerDocument.getUri()).isEqualTo("uri1");
-        assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument");
-        assertThat(customerDocument.getScore()).isEqualTo(1);
-        assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L);
-        assertThat(customerDocument.getPropertyLongArray("longKey1")).asList()
-                .containsExactly(1L, 2L, 3L);
-        assertThat(customerDocument.getPropertyDoubleArray("doubleKey1")).usingExactEquality()
-                .containsExactly(1.0, 2.0, 3.0);
-        assertThat(customerDocument.getPropertyBooleanArray("booleanKey1")).asList()
-                .containsExactly(true, false, true);
-        assertThat(customerDocument.getPropertyStringArray("stringKey1")).asList()
-                .containsExactly("test-value1", "test-value2", "test-value3");
-        assertThat(customerDocument.getPropertyBytesArray("byteKey1")).asList()
-                .containsExactly(sByteArray1, sByteArray2);
-        assertThat(customerDocument.getPropertyDocumentArray("documentKey1")).asList()
-                .containsExactly(sDocumentProperties1, sDocumentProperties2);
-    }
-
-    /**
-     * An example document type for test purposes, defined outside of
-     * {@link GenericDocument} (the way an external developer would define
-     * it).
-     */
-    private static class CustomerDocument extends GenericDocument {
-        private CustomerDocument(GenericDocument document) {
-            super(document);
-        }
-
-        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/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
new file mode 100644
index 0000000..27584a5
--- /dev/null
+++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.people;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PeopleSpaceTileTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    @Test
+    public void testId() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertThat(tile.getId()).isEqualTo("123");
+
+        tile = new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build()).setId(
+                "5").build();
+        assertThat(tile.getId()).isEqualTo("5");
+
+        tile = new PeopleSpaceTile.Builder("12", null, null, null).build();
+        assertThat(tile.getId()).isEqualTo("12");
+    }
+
+    @Test
+    public void testUserName() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertThat(tile.getUserName()).isNull();
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setUserName("Name 1").build();
+        assertThat(tile.getUserName()).isEqualTo("Name 1");
+
+        tile = new PeopleSpaceTile.Builder(null, "Name 2", null, null).build();
+        assertThat(tile.getUserName()).isEqualTo("Name 2");
+    }
+
+    @Test
+    public void testUserIcon() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setUserIcon(
+                Icon.createWithResource(mContext, 1)).build();
+        assertThat(tile.getUserIcon().toString()).isEqualTo(
+                Icon.createWithResource(mContext, 1).toString());
+
+        tile = new PeopleSpaceTile.Builder("12", null, Icon.createWithResource(mContext, 2),
+                null).build();
+        assertThat(tile.getUserIcon().toString()).isEqualTo(
+                Icon.createWithResource(mContext, 2).toString());
+    }
+
+    @Test
+    public void testContactUri() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setContactUri(
+                Uri.parse("test")).build();
+
+        assertThat(tile.getContactUri()).isEqualTo(Uri.parse("test"));
+    }
+
+    @Test
+    public void testUid() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setUid(42).build();
+
+        assertThat(tile.getUid()).isEqualTo(42);
+    }
+
+    @Test
+    public void testPackageName() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        // Automatically added by creating a ShortcutInfo.
+        assertThat(tile.getPackageName()).isEqualTo("com.android.frameworks.coretests");
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setPackageName(
+                "package.name").build();
+        assertThat(tile.getPackageName()).isEqualTo("package.name");
+
+        tile = new PeopleSpaceTile.Builder("12", null, null,
+                new Intent().setPackage("intent.package")).build();
+        assertThat(tile.getPackageName()).isEqualTo("intent.package");
+    }
+
+    @Test
+    public void testLastInteractionTimestamp() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertThat(tile.getLastInteractionTimestamp()).isEqualTo(0L);
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setLastInteractionTimestamp(
+                7L).build();
+        assertThat(tile.getLastInteractionTimestamp()).isEqualTo(7L);
+    }
+
+    @Test
+    public void testImportantConversation() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertFalse(tile.isImportantConversation());
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setIsImportantConversation(
+                true).build();
+        assertTrue(tile.isImportantConversation());
+    }
+
+    @Test
+    public void testHiddenConversation() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertFalse(tile.isHiddenConversation());
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setIsHiddenConversation(
+                true).build();
+        assertTrue(tile.isHiddenConversation());
+    }
+
+    @Test
+    public void testNotification() {
+        Notification notification = new Notification.Builder(mContext, "test").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg" /* pkg */, "pkg" /* opPkg */,
+                1 /* id */, "" /* tag */, 0 /* uid */, 0 /* initialPid */, 0 /* score */,
+                notification, UserHandle.CURRENT, 0 /* postTime */);
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setNotification(sbn).build();
+
+        assertThat(tile.getNotification()).isEqualTo(sbn);
+    }
+
+    @Test
+    public void testIntent() {
+        PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).build();
+        assertThat(tile.getIntent()).isNull();
+
+        tile = new PeopleSpaceTile.Builder(
+                new ShortcutInfo.Builder(mContext, "123").build()).setIntent(new Intent()).build();
+        assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString());
+
+        tile = new PeopleSpaceTile.Builder("12", null, null, new Intent()).build();
+        assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString());
+    }
+
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
new file mode 100644
index 0000000..3757712
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
@@ -0,0 +1,199 @@
+/*
+ * 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 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.junit.Assert.fail;
+
+import android.content.pm.PackageManager.Property;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerPropertyTests {
+
+    @Test
+    public void testBooleanProperty() throws Exception {
+        final Property p =
+                new Property("booleanProperty", true, "android", null);
+        assertTrue(p.isBoolean());
+        assertFalse(p.isFloat());
+        assertFalse(p.isInteger());
+        assertFalse(p.isResourceId());
+        assertFalse(p.isString());
+        assertTrue(p.getBoolean());
+        assertEquals(0.0f, p.getFloat(), 0.0f);
+        assertEquals(0, p.getInteger());
+        assertEquals(0, p.getResourceId());
+        assertEquals(null, p.getString());
+        assertEquals("android", p.getPackageName());
+        assertNull(p.getClassName());
+    }
+
+    @Test
+    public void testBooleanPropertyToBundle() throws Exception {
+        final Bundle b =
+                new Property("booleanProperty", true, "android", null).toBundle(null);
+        assertTrue(b.getBoolean("booleanProperty"));
+    }
+
+    @Test
+    public void testFloatProperty() throws Exception {
+        final Property p =
+                new Property("floatProperty", 3.14f, "android", null);
+        assertFalse(p.isBoolean());
+        assertTrue(p.isFloat());
+        assertFalse(p.isInteger());
+        assertFalse(p.isResourceId());
+        assertFalse(p.isString());
+        assertFalse(p.getBoolean());
+        assertEquals(3.14f, p.getFloat(), 0.0f);
+        assertEquals(0, p.getInteger());
+        assertEquals(0, p.getResourceId());
+        assertEquals(null, p.getString());
+        assertEquals("android", p.getPackageName());
+        assertNull(p.getClassName());
+    }
+
+    @Test
+    public void testFloatPropertyToBundle() throws Exception {
+        final Bundle b =
+                new Property("floatProperty", 3.14f, "android", null).toBundle(null);
+        assertEquals(3.14f, b.getFloat("floatProperty"), 0.0f);
+    }
+
+    @Test
+    public void testIntegerProperty() throws Exception {
+        final Property p =
+                new Property("integerProperty", 42, false, "android", null);
+        assertFalse(p.isBoolean());
+        assertFalse(p.isFloat());
+        assertTrue(p.isInteger());
+        assertFalse(p.isResourceId());
+        assertFalse(p.isString());
+        assertFalse(p.getBoolean());
+        assertEquals(0.0f, p.getFloat(), 0.0f);
+        assertEquals(42, p.getInteger());
+        assertEquals(0, p.getResourceId());
+        assertEquals(null, p.getString());
+        assertEquals("android", p.getPackageName());
+        assertNull(p.getClassName());
+    }
+
+    @Test
+    public void testIntegerPropertyToBundle() throws Exception {
+        final Bundle b =
+                new Property("integerProperty", 42, false, "android", null).toBundle(null);
+        assertEquals(42, b.getInt("integerProperty"));
+    }
+
+    @Test
+    public void testResourceProperty() throws Exception {
+        final Property p =
+                new Property("resourceProperty", 0x7f010001, true, "android", null);
+        assertFalse(p.isBoolean());
+        assertFalse(p.isFloat());
+        assertFalse(p.isInteger());
+        assertTrue(p.isResourceId());
+        assertFalse(p.isString());
+        assertFalse(p.getBoolean());
+        assertEquals(0.0f, p.getFloat(), 0.0f);
+        assertEquals(0, p.getInteger());
+        assertEquals(0x7f010001, p.getResourceId());
+        assertEquals(null, p.getString());
+        assertEquals("android", p.getPackageName());
+        assertNull(p.getClassName());
+    }
+
+    @Test
+    public void testResourcePropertyToBundle() throws Exception {
+        final Bundle b =
+                new Property("resourceProperty", 0x7f010001, true, "android", null).toBundle(null);
+        assertEquals(0x7f010001, b.getInt("resourceProperty"));
+    }
+
+    @Test
+    public void testStringProperty() throws Exception {
+        final Property p =
+                new Property("stringProperty", "koala", "android", null);
+        assertFalse(p.isBoolean());
+        assertFalse(p.isFloat());
+        assertFalse(p.isInteger());
+        assertFalse(p.isResourceId());
+        assertTrue(p.isString());
+        assertFalse(p.getBoolean());
+        assertEquals(0.0f, p.getFloat(), 0.0f);
+        assertEquals(0, p.getInteger());
+        assertEquals(0, p.getResourceId());
+        assertEquals("koala", p.getString());
+        assertEquals("android", p.getPackageName());
+        assertNull(p.getClassName());
+    }
+
+    @Test
+    public void testStringPropertyToBundle() throws Exception {
+        final Bundle b =
+                new Property("stringProperty", "koala", "android", null).toBundle(null);
+        assertEquals("koala", b.getString("stringProperty"));
+    }
+
+    @Test
+    public void testProperty_invalidName() throws Exception {
+        try {
+            final Property p = new Property(null, 1, "android", null);
+            fail("expected assertion error");
+        } catch (AssertionError expected) {
+        }
+    }
+
+    @Test
+    public void testProperty_invalidType() throws Exception {
+        try {
+            final Property p = new Property("invalidTypeProperty", 0, "android", null);
+            fail("expected assertion error");
+        } catch (AssertionError expected) {
+        }
+
+        try {
+            final Property p = new Property("invalidTypeProperty", 6, "android", null);
+            fail("expected assertion error");
+        } catch (AssertionError expected) {
+        }
+
+        try {
+            final Property p = new Property("invalidTypeProperty", -1, "android", null);
+            fail("expected assertion error");
+        } catch (AssertionError expected) {
+        }
+    }
+
+    @Test
+    public void testProperty_noPackageName() throws Exception {
+        try {
+            final Property p = new Property(null, 1, null, null);
+            fail("expected assertion error");
+        } catch (AssertionError expected) {
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index e17800f..7f7bfa3 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -77,7 +77,8 @@
 
         Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
         mTracker = Mockito.spy(
-                new FrameTracker(session, handler, mRenderer, mWrapper));
+                new FrameTracker(session, handler, mRenderer, mWrapper,
+                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1));
         doNothing().when(mTracker).triggerPerfetto();
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index a9cfd28..0ef5643 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -24,6 +24,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -32,6 +33,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.provider.DeviceConfig;
 import android.view.View;
 import android.view.ViewAttachTestActivity;
 
@@ -50,6 +52,7 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -71,8 +74,6 @@
         mView = mActivity.getWindow().getDecorView();
         assertThat(mView.isAttachedToWindow()).isTrue();
 
-        InteractionJankMonitor.abandon();
-
         Handler handler = spy(new Handler(mActivity.getMainLooper()));
         doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
         mWorker = spy(new HandlerThread("Interaction-jank-monitor-test"));
@@ -93,7 +94,8 @@
         Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
         FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                 new ThreadedRendererWrapper(mView.getThreadedRenderer()),
-                new FrameMetricsWrapper()));
+                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
+                /*traceThresholdFrameTimeMillis=*/ -1));
         doReturn(tracker).when(monitor).createFrameTracker(any());
 
         // Simulate a trace session and see if begin / end are invoked.
@@ -104,6 +106,21 @@
     }
 
     @Test
+    public void testDisabledThroughDeviceConfig() {
+        InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+        monitor.init(mView);
+
+        HashMap<String, String> propertiesValues = new HashMap<>();
+        propertiesValues.put("enabled", "false");
+        DeviceConfig.Properties properties = new DeviceConfig.Properties(
+                DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues);
+        monitor.getPropertiesChangedListener().onPropertiesChanged(properties);
+
+        assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+        assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+    }
+
+    @Test
     public void testCheckInitState() {
         InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
 
@@ -134,12 +151,14 @@
         Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
         FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                 new ThreadedRendererWrapper(mView.getThreadedRenderer()),
-                new FrameMetricsWrapper()));
+                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
+                /*traceThresholdFrameTimeMillis=*/ -1));
         doReturn(tracker).when(monitor).createFrameTracker(any());
 
         assertThat(monitor.begin(session.getCuj())).isTrue();
         verify(tracker).begin();
-        verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong());
+        verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(),
+                anyLong());
         Runnable runnable = captor.getValue().getCallback();
         assertThat(runnable).isNotNull();
         mWorker.getThreadHandler().removeCallbacks(runnable);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 3117935..561c549 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -46,7 +46,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
@@ -428,6 +427,19 @@
     }
 
     @Test
+    public void testIgnoreBatteryStatusFlag() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        mDeviceState.setCharging(true);
+        bcs.setIgnoreBatteryStatus(true);
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+        assertEquals(1, bcs.getExportedCallStats().size());
+    }
+
+    @Test
     public void testScreenOff() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 7917a06..fdfc7ac 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -322,6 +322,23 @@
     }
 
     @Test
+    public void testDataCollectedIfIgnoreBatteryStatusFlagSet() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        mDeviceState.setCharging(true);
+        looperStats.setIgnoreBatteryStatus(true);
+
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+                new IllegalArgumentException());
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(1);
+
+    }
+
+    @Test
     public void testScreenStateCollected() {
         TestableLooperStats looperStats = new TestableLooperStats(1, 100);
 
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 4094f83..306388f 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -191,6 +191,10 @@
         }
 
         @Override
+        public void toggleAndFollowTvPower() {
+        }
+
+        @Override
         public void queryDisplayStatus(final IHdmiControlCallback callback) {
         }
 
diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
new file mode 100644
index 0000000..8b42ff7
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.uwb;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link SessionHandle}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SessionHandleTest {
+
+    @Test
+    public void testBasic() {
+        int handleId = 12;
+        SessionHandle handle = new SessionHandle(handleId);
+        assertEquals(handle.getId(), handleId);
+    }
+
+    @Test
+    public void testParcel() {
+        Parcel parcel = Parcel.obtain();
+        SessionHandle handle = new SessionHandle(10);
+        handle.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel);
+        assertEquals(handle, fromParcel);
+    }
+}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 745de84..3e3aefc 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -156,4 +156,11 @@
     sub_dir: "permissions",
     src: "com.android.car.provision.xml",
     filename_from_src: true,
-}
\ No newline at end of file
+}
+
+prebuilt_etc {
+    name: "allowed_privapp_com.android.carshell",
+    sub_dir: "permissions",
+    src: "com.android.car.shell.xml",
+    filename_from_src: true,
+}
diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml
new file mode 100644
index 0000000..32666c8
--- /dev/null
+++ b/data/etc/car/com.android.car.shell.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.shell">
+        <permission name="android.permission.INSTALL_PACKAGES" />
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index fe1182e..e473c55 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -56,5 +56,7 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
+        <permission name="android.permission.READ_DREAM_STATE"/>
+        <permission name="android.permission.READ_DREAM_SUPPRESSION"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index eacb629..52da707 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -241,12 +241,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
-    "-1847087163": {
-      "message": "TRANSIT_TASK_OPEN_BEHIND,  adding %s to mOpeningApps",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-1844540996": {
       "message": "  Initial targets: %s",
       "level": "VERBOSE",
@@ -271,6 +265,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
+    "-1800899273": {
+      "message": "applyAnimation: anim=%s transit=%s Callers=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+      "at": "com\/android\/server\/wm\/AppTransition.java"
+    },
     "-1792633344": {
       "message": "Register task organizer=%s uid=%d",
       "level": "VERBOSE",
@@ -913,12 +913,6 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
-    "-1044506655": {
-      "message": "New transit away from wallpaper: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "-1042574499": {
       "message": "Attempted to add Accessibility overlay window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -985,12 +979,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-928291778": {
-      "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
-      "at": "com\/android\/server\/wm\/AppTransition.java"
-    },
     "-927199900": {
       "message": "Updating global configuration to: %s",
       "level": "INFO",
@@ -1117,12 +1105,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-793346159": {
-      "message": "New transit into wallpaper: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "-784959154": {
       "message": "Attempted to add private presentation window to a non-private display.  Aborting.",
       "level": "WARN",
@@ -1195,6 +1177,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-701167286": {
+      "message": "applyAnimation: transit=%s, enter=%b, wc=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+      "at": "com\/android\/server\/wm\/WindowContainer.java"
+    },
     "-694710814": {
       "message": "Pausing rotation during drag",
       "level": "DEBUG",
@@ -1597,6 +1585,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-240296576": {
+      "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "-235225312": {
       "message": "Skipping config check for initializing activity: %s",
       "level": "VERBOSE",
@@ -1657,12 +1651,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-129722369": {
-      "message": "New transit: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "-118786523": {
       "message": "Resume failed; resetting state to %s: %s",
       "level": "VERBOSE",
@@ -3289,6 +3277,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "1810019902": {
+      "message": "TRANSIT_FLAG_OPEN_BEHIND,  adding %s to mOpeningApps",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1822843721": {
       "message": "Aborted starting %s: startingData=%s",
       "level": "VERBOSE",
@@ -3433,12 +3427,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "2016061474": {
-      "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransition.java"
-    },
     "2018454757": {
       "message": "WS.removeImmediately: %s Already removed...",
       "level": "VERBOSE",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5743df5..712349a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -169,6 +169,8 @@
     @UnsupportedAppUsage
     public long native_instance;
 
+    private Runnable mCleaner;
+
     /** @hide */
     @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
     @Retention(RetentionPolicy.SOURCE)
@@ -808,18 +810,16 @@
          */
         public @NonNull Typeface build() {
             final int userFallbackSize = mFamilies.size();
-            final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
-            final long[] ptrArray = new long[fallback.length + userFallbackSize];
+            final Typeface fallbackTypeface = getSystemDefaultTypeface(mFallbackName);
+            final long[] ptrArray = new long[userFallbackSize];
             for (int i = 0; i < userFallbackSize; ++i) {
                 ptrArray[i] = mFamilies.get(i).getNativePtr();
             }
-            for (int i = 0; i < fallback.length; ++i) {
-                ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
-            }
             final int weight = mStyle == null ? 400 : mStyle.getWeight();
             final int italic =
                     (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ?  0 : 1;
-            return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
+            return new Typeface(nativeCreateFromArray(
+                    ptrArray, fallbackTypeface.native_instance, weight, italic));
         }
     }
 
@@ -1056,7 +1056,8 @@
             ptrArray[i] = families[i].mNativePtr;
         }
         return new Typeface(nativeCreateFromArray(
-                ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+                ptrArray, 0, RESOLVE_BY_FONT_TABLE,
+                RESOLVE_BY_FONT_TABLE));
     }
 
     /**
@@ -1069,7 +1070,7 @@
         for (int i = 0; i < families.length; ++i) {
             ptrArray[i] = families[i].getNativePtr();
         }
-        return new Typeface(nativeCreateFromArray(ptrArray,
+        return new Typeface(nativeCreateFromArray(ptrArray, 0,
                   RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     }
 
@@ -1104,15 +1105,13 @@
     @Deprecated
     private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families,
                 String fallbackName, int weight, int italic) {
-        android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName);
-        long[] ptrArray = new long[families.length + fallback.length];
+        final Typeface fallbackTypeface = getSystemDefaultTypeface(fallbackName);
+        long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; i++) {
             ptrArray[i] = families[i].mNativePtr;
         }
-        for (int i = 0; i < fallback.length; i++) {
-            ptrArray[i + families.length] = fallback[i].getNativePtr();
-        }
-        return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
+        return new Typeface(nativeCreateFromArray(
+                ptrArray, fallbackTypeface.native_instance, weight, italic));
     }
 
     // don't allow clients to call this directly
@@ -1123,7 +1122,7 @@
         }
 
         native_instance = ni;
-        sRegistry.registerNativeAllocation(this, native_instance);
+        mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
     }
@@ -1237,6 +1236,13 @@
         bos.write(value & 0xFF);
     }
 
+    /** @hide */
+    public static Map<String, Typeface> getSystemFontMap() {
+        synchronized (SYSTEM_FONT_MAP_LOCK) {
+            return sSystemFontMap;
+        }
+    }
+
     /**
      * Deserialize font map and set it as system font map. This method should be called at most once
      * per process.
@@ -1297,13 +1303,33 @@
         }
     }
 
-    static {
+    /** @hide */
+    @VisibleForTesting
+    public static void destroySystemFontMap() {
+        synchronized (SYSTEM_FONT_MAP_LOCK) {
+            for (Typeface typeface : sSystemFontMap.values()) {
+                typeface.mCleaner.run();
+            }
+            sSystemFontMap.clear();
+            if (sSystemFontMapBuffer != null) {
+                SharedMemory.unmap(sSystemFontMapBuffer);
+            }
+            sSystemFontMapBuffer = null;
+        }
+    }
+
+    /** @hide */
+    public static void loadPreinstalledSystemFontMap() {
         final HashMap<String, Typeface> systemFontMap = new HashMap<>();
         initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
                 SystemFonts.getAliases());
         setSystemFontMap(systemFontMap);
     }
 
+    static {
+        loadPreinstalledSystemFontMap();
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -1350,7 +1376,8 @@
     @UnsupportedAppUsage
     private static native long nativeCreateWeightAlias(long native_instance, int weight);
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
+    private static native long nativeCreateFromArray(
+            long[] familyArray, long fallbackTypeface, int weight, int italic);
     private static native int[] nativeGetSupportedAxes(long native_instance);
 
     @CriticalNative
diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING
new file mode 100644
index 0000000..1018702
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/TEST_MAPPING
@@ -0,0 +1,24 @@
+{
+  "presubmit": [
+    {
+
+      "name": "CtsGraphicsTestCases",
+      "file_patterns": ["(/|^)Icon\\.java"],
+      "options" : [
+        {
+          "include-filter": "android.graphics.drawable.cts.IconTest"
+        }
+      ]
+    },
+    {
+
+      "name": "FrameworksCoreTests",
+      "file_patterns": ["(/|^)Icon\\.java"],
+      "options" : [
+        {
+          "include-filter": "android.graphics.drawable.IconTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 95a8417..95c4706 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -67,19 +67,6 @@
     }
 
     /**
-     * Returns fallback list for the given family name.
-     *
-     * If no fallback found for the given family name, returns fallback for the default family.
-     *
-     * @param familyName family name, e.g. "serif"
-     * @hide
-     */
-    public static @NonNull FontFamily[] getSystemFallback(@Nullable String familyName) {
-        final FontFamily[] families = sSystemFallbackMap.get(familyName);
-        return families == null ? sSystemFallbackMap.get(DEFAULT_FAMILY) : families;
-    }
-
-    /**
      * Returns raw system fallback map.
      *
      * This method is intended to be used only by Typeface static initializer.
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 9af15a5..49a4887 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -17,11 +17,11 @@
 package android.security;
 
 import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keymaster.KeymasterDefs;
 import android.system.keystore2.IKeystoreOperation;
-import android.system.keystore2.KeyParameter;
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
 
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 9d3b622..7c3de8b 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.app.compat.CompatChanges;
+import android.hardware.keymint.KeyParameter;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keystore.BackendBusyException;
@@ -27,7 +28,6 @@
 import android.system.keystore2.IKeystoreSecurityLevel;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
 
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 3ac9d68..5867ef6 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -84,7 +84,7 @@
 
         // java.security.KeyStore
         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
-        put("alg.alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore");
+        put("Alg.Alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore");
 
         // java.security.KeyPairGenerator
         put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 70713a4..69c7a25 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -17,10 +17,10 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
 
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index dd094b7..2b5f6c3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -18,13 +18,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyProperties;
 import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
-import android.system.keystore2.KeyParameter;
 
 import libcore.util.EmptyArray;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index b785ee5..18d2692 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -19,11 +19,11 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyStoreCryptoOperation;
-import android.system.keystore2.KeyParameter;
 
 import libcore.util.EmptyArray;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 9f7f238..2250c89 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,12 +17,12 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
 
 import libcore.util.EmptyArray;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 3dde2e5..eea45c2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -16,12 +16,12 @@
 
 package android.security.keystore2;
 
+import android.hardware.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyStoreCryptoOperation;
 import android.security.keystore.KeymasterUtils;
-import android.system.keystore2.KeyParameter;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index ccd0a4b..479fd8a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -16,6 +16,8 @@
 
 package android.security.keystore2;
 
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
 import android.security.KeyStore2;
 import android.security.KeyStoreSecurityLevel;
 import android.security.keymaster.KeymasterArguments;
@@ -29,8 +31,6 @@
 import android.system.keystore2.IKeystoreSecurityLevel;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
-import android.system.keystore2.SecurityLevel;
 import android.util.Log;
 
 import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index a747a0e..61725e3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,6 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
 import android.os.Build;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore2;
@@ -35,9 +37,7 @@
 import android.system.keystore2.IKeystoreSecurityLevel;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
 import android.system.keystore2.ResponseCode;
-import android.system.keystore2.SecurityLevel;
 import android.util.Log;
 
 import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index a6ea972..2686ddc 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,11 +18,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeymasterUtils;
 import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
 
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 5f1b9c0..444dad4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -17,9 +17,9 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
 
 import java.security.InvalidKeyException;
 import java.security.SignatureSpi;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index 55414b7..a168f8f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -18,12 +18,12 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyStoreCryptoOperation;
-import android.system.keystore2.KeyParameter;
 
 import libcore.util.EmptyArray;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 4c26864..9790a4a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -18,6 +18,9 @@
 
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricManager;
+import android.hardware.keymint.HardwareAuthenticatorType;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
 import android.security.GateKeeper;
 import android.security.KeyStore2;
 import android.security.KeyStoreParameter;
@@ -36,9 +39,7 @@
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyEntryResponse;
 import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
 import android.system.keystore2.ResponseCode;
-import android.system.keystore2.SecurityLevel;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -871,16 +872,13 @@
         List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
 
         AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
-        // TODO Replace with HardwareAuthenticatorType.PASSWORD when KeyMint AIDL spec has landed.
-        authenticatorSpec.authenticatorType = 1; // HardwareAuthenticatorType.PASSWORD
+        authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD;
         authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
         authenticatorSpecs.add(authenticatorSpec);
 
         for (long sid : biometricSids) {
             AuthenticatorSpec authSpec = new AuthenticatorSpec();
-            // TODO Replace with HardwareAuthenticatorType.FINGERPRINT when KeyMint AIDL spec has
-            //      landed.
-            authSpec.authenticatorType = 2; // HardwareAuthenticatorType.FINGERPRINT
+            authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT;
             authSpec.authenticatorId = sid;
             authenticatorSpecs.add(authSpec);
         }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 3d5a8f6..a2d4528 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -18,10 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.ArrayUtils;
 import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
 
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index ee67ed3..8fa532b 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -18,13 +18,13 @@
 
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricManager;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
 import android.security.GateKeeper;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.UserAuthArgs;
 import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
-import android.system.keystore2.SecurityLevel;
 
 import java.security.ProviderException;
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
new file mode 100644
index 0000000..b86f36a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -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.
+  -->
+
+<com.android.wm.shell.apppairs.DividerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <View
+        style="@style/DockedDividerBackground"
+        android:id="@+id/docked_divider_background"
+        android:background="@color/docked_divider_background"/>
+
+</com.android.wm.shell.apppairs.DividerView>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 6c7fade..7b5bda7 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -28,7 +28,7 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
-    <string name="dock_forced_resizable" msgid="1749750436092293116">"يمكن ألا يعمل التطبيق مع وضع تقسيم الشاشة."</string>
+    <string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string>
@@ -45,7 +45,7 @@
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"عرض النافذة السفلية بملء الشاشة"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استخدام وضع \"التصفح بيد واحدة\""</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"للخروج، مرِّر سريعًا من أسفل الشاشة إلى أعلاها أو انقر في أي مكان فوق التطبيق."</string>
-    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"تفعيل وضع \"التصفح بيد واحدة\""</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"بدء وضع \"التصفح بيد واحدة\""</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"الخروج من وضع \"التصفح بيد واحدة\""</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"إعدادات فقاعات المحادثات على <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"القائمة الكاملة"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index 973ae8e..d33bf99 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -20,5 +20,5 @@
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Відарыс у відарысе"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
     <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
-    <string name="pip_fullscreen" msgid="7278047353591302554">"Ва ўвесь экран"</string>
+    <string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 4979a40..f04796a 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -28,8 +28,7 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
-    <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
-    <skip />
+    <string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
@@ -44,14 +43,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string>
-    <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
-    <skip />
-    <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
-    <skip />
-    <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Einhandmodus wird verwendet"</string>
+    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Wenn du die App schließen möchtest, wische vom unteren Rand des Displays nach oben oder tippe auf eine beliebige Stelle oberhalb der App"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Einhandmodus starten"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Einhandmodus beenden"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Einstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g>-Bubbles"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mehr anzeigen"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Wieder dem Stapel hinzufügen"</string>
@@ -68,8 +63,7 @@
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
-    <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
-    <skip />
+    <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 0432f1c..02cce9d 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -17,8 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
-    <skip />
+    <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bild im Bild"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index dfa4b22..e675861 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -28,8 +28,7 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
-    <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
-    <skip />
+    <string name="dock_forced_resizable" msgid="1749750436092293116">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"രണ്ടാം ഡിസ്‌പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string>
@@ -44,14 +43,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"മുകളിൽ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"മുകളിൽ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"താഴെ പൂർണ്ണ സ്ക്രീൻ"</string>
-    <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
-    <skip />
-    <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
-    <skip />
-    <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ഒറ്റക്കൈ മോഡ് എങ്ങനെ ഉപയോഗിക്കാം"</string>
+    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"പുറത്ത് കടക്കാൻ, സ്ക്രീനിന്റെ ചുവടെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ആപ്പിന് മുകളിലായി എവിടെയെങ്കിലും ടാപ്പ് ചെയ്യുക"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ഒറ്റക്കൈ മോഡ് ആരംഭിച്ചു"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"ഒറ്റക്കൈ മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബബിളുകളുടെ ക്രമീകരണം"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ഓവർഫ്ലോ"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"അടുക്കുകളിലേക്ക് തിരിച്ച് ചേർക്കുക"</string>
@@ -68,8 +63,7 @@
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ഈ ആപ്പിൽ നിന്നുള്ള ബബിളുകൾ ഓഫാക്കാൻ മാനേജ് ചെയ്യുക ടാപ്പ് ചെയ്യുക"</string>
-    <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
-    <skip />
+    <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index 7aaf79f..c74e0bb 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -17,8 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
-    <skip />
+    <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ചിത്രത്തിനുള്ളിൽ ചിത്രം"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
     <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index cd6529f0..4b0adc6 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -28,8 +28,7 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
-    <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
-    <skip />
+    <string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string>
@@ -44,14 +43,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"اوپر %50"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"اوپر %30"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"نچلی فل اسکرین"</string>
-    <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
-    <skip />
-    <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
-    <skip />
-    <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ایک ہاتھ کی وضع کا استعمال کرنا"</string>
+    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"باہر نکلنے کیلئے، اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں یا ایپ کے اوپر کہیں بھی تھپتھپائیں"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ایک ہاتھ کی وضع شروع کریں"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"ایک ہاتھ کی وضع سے باہر نکلیں"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> بلبلوں کے لیے ترتیبات"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"اوورفلو"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"انبار میں واپس شامل کریں"</string>
@@ -68,8 +63,7 @@
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"اس ایپ سے بلبلوں کو آف کرنے کے لیے نظم کریں پر تھپتھپائیں"</string>
-    <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
-    <skip />
+    <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"سمجھ آ گئی"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index 64e5db5..3179533 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -17,8 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
-    <skip />
+    <string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر میں تصویر"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
     <string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index c913e0c..f0eae97 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -30,6 +30,12 @@
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">250</integer>
 
+    <!-- Animation duration for translating of one handed when trigger / dismiss. -->
+    <integer name="config_one_handed_translate_animation_duration">300</integer>
+
+    <!-- One handed mode default offset % of display size -->
+    <fraction name="config_one_handed_offset">40%</fraction>
+
     <!-- Allow one handed to enable round corner -->
     <bool name="config_one_handed_enable_round_corner">true</bool>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index d30acee..f199072 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
 import java.io.PrintWriter;
@@ -41,8 +42,6 @@
  * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
  * Also includes all UI for managing the pair like the divider.
  */
-// TODO: Add divider
-// TODO: Handle display rotation
 class AppPair implements ShellTaskOrganizer.TaskListener {
     private static final String TAG = AppPair.class.getSimpleName();
 
@@ -55,10 +54,13 @@
 
     private final AppPairsController mController;
     private final SyncTransactionQueue mSyncQueue;
+    private final DisplayController mDisplayController;
+    private AppPairLayout mAppPairLayout;
 
     AppPair(AppPairsController controller) {
         mController = controller;
         mSyncQueue = controller.getSyncTransactionQueue();
+        mDisplayController = controller.getDisplayController();
     }
 
     int getRootTaskId() {
@@ -90,13 +92,12 @@
 
         mTaskInfo1 = task1;
         mTaskInfo2 = task2;
+        mAppPairLayout = new AppPairLayout(
+                mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
+                mDisplayController.getDisplay(mRootTaskInfo.displayId),
+                mRootTaskInfo.configuration,
+                mRootTaskLeash);
 
-        // TODO: properly calculate bounds for pairs.
-        final Rect rootBounds = mRootTaskInfo.configuration.windowConfiguration.getBounds();
-        final Rect bounds1 = new Rect(
-                rootBounds.left, rootBounds.top, rootBounds.right / 2, rootBounds.bottom / 2);
-        final Rect bounds2 = new Rect(
-                bounds1.right, bounds1.bottom, rootBounds.right, rootBounds.bottom);
         final WindowContainerToken token1 = task1.token;
         final WindowContainerToken token2 = task2.token;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -106,8 +107,8 @@
                 .reparent(token2, mRootTaskInfo.token, true /* onTop */)
                 .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
                 .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
-                .setBounds(token1, bounds1)
-                .setBounds(token2, bounds2)
+                .setBounds(token1, mAppPairLayout.getBounds1())
+                .setBounds(token2, mAppPairLayout.getBounds2())
                 // Moving the root task to top after the child tasks were repareted , or the root
                 // task cannot be visible and focused.
                 .reorder(mRootTaskInfo.token, true);
@@ -131,6 +132,15 @@
 
         mTaskInfo1 = null;
         mTaskInfo2 = null;
+        mAppPairLayout.release();
+        mAppPairLayout = null;
+    }
+
+    void setVisible(boolean visible) {
+        if (mAppPairLayout == null) {
+            return;
+        }
+        mAppPairLayout.setDividerVisibility(visible);
     }
 
     @Override
@@ -150,13 +160,20 @@
 
         if (mTaskLeash1 == null || mTaskLeash2 == null) return;
 
+        setVisible(true);
+        final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
+        final Rect dividerBounds = mAppPairLayout.getDividerBounds();
+
         // TODO: Is there more we need to do here?
         mSyncQueue.runInSync(t -> t
                 .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
                         mTaskInfo1.positionInParent.y)
                 .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
                         mTaskInfo2.positionInParent.y)
+                .setLayer(dividerLeash, Integer.MAX_VALUE)
+                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                 .show(mRootTaskLeash)
+                .show(dividerLeash)
                 .show(mTaskLeash1)
                 .show(mTaskLeash2));
     }
@@ -165,6 +182,24 @@
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
         if (taskInfo.taskId == getRootTaskId()) {
             mRootTaskInfo = taskInfo;
+
+            if (mAppPairLayout != null
+                    && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+                // Update bounds when there is root bounds or orientation changed.
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
+                final Rect dividerBounds = mAppPairLayout.getDividerBounds();
+                final Rect bounds1 = mAppPairLayout.getBounds1();
+                final Rect bounds2 = mAppPairLayout.getBounds2();
+
+                wct.setBounds(mTaskInfo1.token, bounds1)
+                        .setBounds(mTaskInfo2.token, bounds2);
+                mController.getTaskOrganizer().applyTransaction(wct);
+                mSyncQueue.runInSync(t -> t
+                        .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
+                        .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
+                        .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top));
+            }
         } else if (taskInfo.taskId == getTaskId1()) {
             mTaskInfo1 = taskInfo;
         } else if (taskInfo.taskId == getTaskId2()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
new file mode 100644
index 0000000..f8703f7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
@@ -0,0 +1,224 @@
+/*
+ * 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.apppairs;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Records and handles layout of a pair of apps.
+ */
+// TODO(172704238): add tests
+final class AppPairLayout {
+    private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider";
+    private final Context mContext;
+    private final AppPairWindowManager mAppPairWindowManager;
+    private final SurfaceControlViewHost mViewHost;
+
+    private final int mDividerWindowWidth;
+    private final int mDividerWindowInsets;
+
+    private boolean mIsLandscape;
+    private Rect mRootBounds;
+    private DIVIDE_POLICY mDividePolicy;
+
+    private DividerView mDividerView;
+    private SurfaceControl mDividerLeash;
+
+    AppPairLayout(
+            Context context,
+            Display display,
+            Configuration configuration,
+            SurfaceControl rootLeash) {
+        mContext = context.createConfigurationContext(configuration);
+        mIsLandscape = isLandscape(configuration);
+        mRootBounds = configuration.windowConfiguration.getBounds();
+        mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+        mDividerWindowInsets = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_insets);
+
+        mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash);
+        mViewHost = new SurfaceControlViewHost(mContext, display, mAppPairWindowManager);
+        mDividePolicy = DIVIDE_POLICY.MIDDLE;
+        mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+    }
+
+    boolean updateConfiguration(Configuration configuration) {
+        mAppPairWindowManager.setConfiguration(configuration);
+        final Rect rootBounds = configuration.windowConfiguration.getBounds();
+        final boolean isLandscape = isLandscape(configuration);
+        if (mIsLandscape == isLandscape && isIdenticalBounds(mRootBounds, rootBounds)) {
+            return false;
+        }
+
+        mIsLandscape = isLandscape;
+        mRootBounds = rootBounds;
+        mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+        mViewHost.relayout(
+                mDividePolicy.mDividerBounds.width(),
+                mDividePolicy.mDividerBounds.height());
+        // TODO(172704238): handle divider bar rotation.
+        return true;
+    }
+
+    Rect getBounds1() {
+        return mDividePolicy.mBounds1;
+    }
+
+    Rect getBounds2() {
+        return mDividePolicy.mBounds2;
+    }
+
+    Rect getDividerBounds() {
+        return mDividePolicy.mDividerBounds;
+    }
+
+    SurfaceControl getDividerLeash() {
+        return mDividerLeash;
+    }
+
+    void release() {
+        if (mViewHost == null) return;
+        mViewHost.release();
+    }
+
+    void setDividerVisibility(boolean visible) {
+        if (mDividerView == null) {
+            initDivider();
+        }
+        if (visible) {
+            mDividerView.show();
+        } else {
+            mDividerView.hide();
+        }
+    }
+
+    private void initDivider() {
+        final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
+                .inflate(R.layout.split_divider, null);
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                mDividePolicy.mDividerBounds.width(),
+                mDividePolicy.mDividerBounds.height(),
+                TYPE_DOCK_DIVIDER,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+                        | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
+                PixelFormat.TRANSLUCENT);
+        lp.token = new Binder();
+        lp.setTitle(DIVIDER_WINDOW_TITLE);
+        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+
+        mViewHost.setView(dividerView, lp);
+        mDividerView = dividerView;
+        mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken());
+    }
+
+    private static boolean isLandscape(Configuration configuration) {
+        return configuration.orientation == ORIENTATION_LANDSCAPE;
+    }
+
+    private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) {
+        return bounds1.left == bounds2.left && bounds1.top == bounds2.top
+                && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom;
+    }
+
+    /**
+     * Indicates the policy of placing divider bar and corresponding split-screens.
+     */
+    // TODO(172704238): add more divide policy and provide snap to resize feature for divider bar.
+    enum DIVIDE_POLICY {
+        MIDDLE;
+
+        void update(boolean isLandscape, Rect rootBounds, int dividerWindowWidth,
+                int dividerWindowInsets) {
+            final int dividerOffset = dividerWindowWidth / 2;
+            final int boundsOffset = dividerOffset - dividerWindowInsets;
+
+            mDividerBounds = new Rect(rootBounds);
+            mBounds1 = new Rect(rootBounds);
+            mBounds2 = new Rect(rootBounds);
+
+            switch (this) {
+                case MIDDLE:
+                default:
+                    if (isLandscape) {
+                        mDividerBounds.left = rootBounds.width() / 2 - dividerOffset;
+                        mDividerBounds.right = rootBounds.width() / 2 + dividerOffset;
+                        mBounds1.left = rootBounds.width() / 2 + boundsOffset;
+                        mBounds2.right = rootBounds.width() / 2 - boundsOffset;
+                    } else {
+                        mDividerBounds.top = rootBounds.height() / 2 - dividerOffset;
+                        mDividerBounds.bottom = rootBounds.height() / 2 + dividerOffset;
+                        mBounds1.bottom = rootBounds.height() / 2 - boundsOffset;
+                        mBounds2.top = rootBounds.height() / 2 + boundsOffset;
+                    }
+            }
+        }
+
+        Rect mDividerBounds;
+        Rect mBounds1;
+        Rect mBounds2;
+    }
+
+    /**
+     * WindowManger for app pair. Holds view hierarchy for the root task.
+     */
+    private static final class AppPairWindowManager extends WindowlessWindowManager {
+        AppPairWindowManager(Configuration config, SurfaceControl rootSurface) {
+            super(config, rootSurface, null /* hostInputToken */);
+        }
+
+        @Override
+        public void setTouchRegion(IBinder window, Region region) {
+            super.setTouchRegion(window, region);
+        }
+
+        @Override
+        public SurfaceControl getSurfaceControl(IWindow window) {
+            return super.getSurfaceControl(window);
+        }
+
+        @Override
+        public void setConfiguration(Configuration configuration) {
+            super.setConfiguration(configuration);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index ef3e3e0..af06764 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -36,4 +36,6 @@
     void dump(@NonNull PrintWriter pw, String prefix);
     /** Called when the shell organizer has been registered. */
     void onOrganizerRegistered();
+    /** Called when the visibility of the keyguard changes. */
+    void onKeyguardVisibilityChanged(boolean showing);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index e0c7ba9..925a4f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.apppairs;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
 
 import android.app.ActivityManager;
@@ -26,14 +28,17 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import java.io.PrintWriter;
 
 /**
  * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
  */
-public class AppPairsController implements AppPairs {
+public class AppPairsController implements AppPairs, TaskStackListenerCallback {
     private static final String TAG = AppPairsController.class.getSimpleName();
 
     private final ShellTaskOrganizer mTaskOrganizer;
@@ -42,10 +47,15 @@
     private AppPairsPool mPairsPool;
     // Active app-pairs mapped by root task id key.
     private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
+    private final DisplayController mDisplayController;
+    private int mForegroundTaskId = INVALID_TASK_ID;
 
-    public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+    public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+                DisplayController displayController, TaskStackListenerImpl taskStackListener) {
         mTaskOrganizer = organizer;
         mSyncQueue = syncQueue;
+        mDisplayController = displayController;
+        taskStackListener.addListener(this);
     }
 
     @Override
@@ -61,6 +71,27 @@
     }
 
     @Override
+    public void onTaskMovedToFront(int taskId) {
+        mForegroundTaskId = INVALID_TASK_ID;
+        for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+            final AppPair candidate = mActiveAppPairs.valueAt(i);
+            final boolean containForegroundTask = candidate.contains(taskId);
+            candidate.setVisible(containForegroundTask);
+            if (containForegroundTask) {
+                mForegroundTaskId = candidate.getRootTaskId();
+            }
+        }
+    }
+
+    @Override
+    public void onKeyguardVisibilityChanged(boolean showing) {
+        if (mForegroundTaskId == INVALID_TASK_ID) {
+            return;
+        }
+        mActiveAppPairs.get(mForegroundTaskId).setVisible(!showing);
+    }
+
+    @Override
     public boolean pair(int taskId1, int taskId2) {
         final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
         final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
@@ -127,6 +158,10 @@
         return mSyncQueue;
     }
 
+    DisplayController getDisplayController() {
+        return mDisplayController;
+    }
+
     @Override
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
new file mode 100644
index 0000000..41b5e47
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
@@ -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.
+ */
+
+package com.android.wm.shell.apppairs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Stack divider for app pair.
+ */
+public class DividerView extends FrameLayout {
+    public DividerView(@NonNull Context context) {
+        super(context);
+    }
+
+    public DividerView(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    void show() {
+        post(() -> setVisibility(View.VISIBLE));
+    }
+
+    void hide() {
+        post(() -> setVisibility(View.INVISIBLE));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index beac59b..aa7355b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -89,6 +89,7 @@
 
     // TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast
     public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged";
+    public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated";
     public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened";
     public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible";
     public static final String EXTRA_TASKBAR_POSITION = "taskbarPosition";
@@ -350,12 +351,15 @@
                 + " itemPosition: " + itemPosition[0] + "," + itemPosition[1]
                 + " iconSize: " + iconSize);
         PointF point = new PointF(itemPosition[0], itemPosition[1]);
-        mBubblePositioner.setPinnedLocation(point);
+        mBubblePositioner.setPinnedLocation(isVisible ? point : null);
         mBubblePositioner.updateForTaskbar(iconSize, taskbarPosition, isVisible, taskbarSize);
         if (mStackView != null) {
-            if (isVisible) {
-                mStackView.updateStackPosition();
+            if (isVisible && b.getBoolean(EXTRA_TASKBAR_CREATED, false /* default */)) {
+                // If taskbar was created, add and remove the window so that bubbles display on top
+                removeFromWindowManagerMaybe();
+                addToWindowManagerMaybe();
             }
+            mStackView.updateStackPosition();
             mBubbleIconFactory = new BubbleIconFactory(mContext);
             mStackView.onDisplaySizeChanged();
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index f84936e..e8c6cb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -37,6 +37,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.TaskStackListenerCallback;
@@ -205,8 +206,11 @@
         mGestureHandler = gestureHandler;
         mOverlayManager = overlayManager;
 
-        mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50)
-                / 100.0f;
+        final float offsetPercentageConfig = context.getResources().getFraction(
+                R.fraction.config_one_handed_offset, 1, 1);
+        final int sysPropPercentageConfig = SystemProperties.getInt(
+                ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
+        mOffSetFraction = sysPropPercentageConfig / 100.0f;
         mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                 context.getContentResolver());
         mIsSwipeToNotificationEnabled =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index bd6c1e0..0311030 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -40,6 +40,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.os.SomeArgs;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
 
 import java.io.PrintWriter;
@@ -156,8 +157,11 @@
         mDisplayController = displayController;
         mDefaultDisplayBounds.set(getDisplayBounds());
         mLastVisualDisplayBounds.set(getDisplayBounds());
+        final int animationDurationConfig = context.getResources().getInteger(
+                R.integer.config_one_handed_translate_animation_duration);
         mEnterExitAnimationDurationMs =
-                SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION, 300);
+                SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
+                        animationDurationConfig);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mTutorialHandler = tutorialHandler;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index b6b518d..d65ad62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -86,8 +86,11 @@
                 context.getSystemService(Context.ACCESSIBILITY_SERVICE);
         mTargetViewContainer = new FrameLayout(context);
         mTargetViewContainer.setClipChildren(false);
-        mTutorialAreaHeight = Math.round(mDisplaySize.y
-                * (SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f));
+        final float offsetPercentageConfig = context.getResources().getFraction(
+                R.fraction.config_one_handed_offset, 1, 1);
+        final int sysPropPercentageConfig = SystemProperties.getInt(
+                ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
+        mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f));
         mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
         mTargetViewContainer.addView(mTutorialView);
         mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index df6683e..1bb5eda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.Size;
@@ -42,6 +43,9 @@
     private final @NonNull PipBoundsState mPipBoundsState;
     private final PipSnapAlgorithm mSnapAlgorithm;
 
+    private float mDefaultSizePercent;
+    private float mMinAspectRatioForMinSize;
+    private float mMaxAspectRatioForMinSize;
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
@@ -51,7 +55,7 @@
 
     public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) {
         mPipBoundsState = pipBoundsState;
-        mSnapAlgorithm = new PipSnapAlgorithm(context);
+        mSnapAlgorithm = new PipSnapAlgorithm();
         reloadResources(context);
         // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
         // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
@@ -83,6 +87,11 @@
                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
         mMaxAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+        mDefaultSizePercent = res.getFloat(
+                com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
+        mMaxAspectRatioForMinSize = res.getFloat(
+                com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+        mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
     }
 
     /**
@@ -174,7 +183,7 @@
             final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
                     : defaultMinEdgeSize;
             // Use the existing size but adjusted to the aspect ratio and min edge size.
-            size = mSnapAlgorithm.getSizeForAspectRatio(
+            size = getSizeForAspectRatio(
                     new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
         } else {
             if (overrideMinSize != null) {
@@ -184,7 +193,7 @@
             } else {
                 // Calculate the default size using the display size and default min edge size.
                 final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
-                size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
+                size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
                         displayInfo.logicalWidth, displayInfo.logicalHeight);
             }
         }
@@ -229,7 +238,7 @@
                 defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
             } else {
                 // Calculate the default size using the display size and default min edge size.
-                defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+                defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
                         mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
             }
             Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(),
@@ -270,13 +279,28 @@
         getInsetBounds(movementBounds);
 
         // Apply the movement bounds adjustments based on the current state.
-        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+        getMovementBounds(stackBounds, movementBounds, movementBounds,
                 (adjustForIme && mPipBoundsState.isImeShowing())
                         ? mPipBoundsState.getImeHeight() : 0);
+
         return movementBounds;
     }
 
     /**
+     * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds.
+     */
+    public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
+            int bottomOffset) {
+        // Adjust the right/bottom to ensure the stack bounds never goes offscreen
+        movementBoundsOut.set(insetBounds);
+        movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right
+                - stackBounds.width());
+        movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom
+                - stackBounds.height());
+        movementBoundsOut.bottom -= bottomOffset;
+    }
+
+    /**
      * @return the default snap fraction to apply instead of the default gravity when calculating
      *         the default stack bounds when first entering PiP.
      */
@@ -304,6 +328,62 @@
     }
 
     /**
+     * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge
+     * is at least minEdgeSize.
+     */
+    public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
+            int displayHeight) {
+        final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
+        final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
+
+        final int width;
+        final int height;
+        if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
+            // Beyond these points, we can just use the min size as the shorter edge
+            if (aspectRatio <= 1) {
+                // Portrait, width is the minimum size
+                width = minSize;
+                height = Math.round(width / aspectRatio);
+            } else {
+                // Landscape, height is the minimum size
+                height = minSize;
+                width = Math.round(height * aspectRatio);
+            }
+        } else {
+            // Within these points, we ensure that the bounds fit within the radius of the limits
+            // at the points
+            final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+            final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+            height = (int) Math.round(Math.sqrt((radius * radius)
+                    / (aspectRatio * aspectRatio + 1)));
+            width = Math.round(height * aspectRatio);
+        }
+        return new Size(width, height);
+    }
+
+    /**
+     * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
+     * minimum edge is at least minEdgeSize.
+     */
+    public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
+        final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+        final int minSize = (int) Math.max(minEdgeSize, smallestSize);
+
+        final int width;
+        final int height;
+        if (aspectRatio <= 1) {
+            // Portrait, width is the minimum size.
+            width = minSize;
+            height = Math.round(width / aspectRatio);
+        } else {
+            // Landscape, height is the minimum size
+            height = minSize;
+            width = Math.round(height * aspectRatio);
+        }
+        return new Size(width, height);
+    }
+
+    /**
      * Dumps internal states.
      */
     public void dump(PrintWriter pw, String prefix) {
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 ce1139b..53aa614 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
@@ -59,42 +59,39 @@
     private final @NonNull Rect mExpandedBounds = new Rect();
     private final @NonNull Rect mNormalMovementBounds = new Rect();
     private final @NonNull Rect mExpandedMovementBounds = new Rect();
-    private final Context mContext;
+    private final @NonNull Context mContext;
     private float mAspectRatio;
     private int mStashedState = STASH_TYPE_NONE;
     private int mStashOffset;
-    private PipReentryState mPipReentryState;
-    private ComponentName mLastPipComponentName;
-    private final DisplayInfo mDisplayInfo = new DisplayInfo();
-    private final DisplayLayout mDisplayLayout = new DisplayLayout();
+    private @Nullable PipReentryState mPipReentryState;
+    private @Nullable ComponentName mLastPipComponentName;
+    private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo();
+    private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
     /** The current minimum edge size of PIP. */
     private int mMinEdgeSize;
     /** The preferred minimum (and default) size specified by apps. */
-    private Size mOverrideMinSize;
-    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();
+    private @Nullable Size mOverrideMinSize;
+    private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
     private boolean mIsImeShowing;
     private int mImeHeight;
     private boolean mIsShelfShowing;
     private int mShelfHeight;
 
-    private Runnable mOnMinimalSizeChangeCallback;
-    private BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback;
+    private @Nullable Runnable mOnMinimalSizeChangeCallback;
+    private @Nullable BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback;
 
-    public PipBoundsState(Context context) {
+    public PipBoundsState(@NonNull Context context) {
         mContext = context;
         reloadResources();
     }
 
-    /**
-     * Reloads the resources.
-     */
+    /** Reloads the resources. */
     public void onConfigurationChanged() {
         reloadResources();
     }
 
     private void reloadResources() {
-        mStashOffset = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.pip_stash_offset);
+        mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset);
     }
 
     /** Set the current PIP bounds. */
@@ -102,12 +99,14 @@
         mBounds.set(bounds);
     }
 
+    /** Get the current PIP bounds. */
     @NonNull
     public Rect getBounds() {
         return new Rect(mBounds);
     }
 
     /** Returns the current movement bounds. */
+    @NonNull
     public Rect getMovementBounds() {
         return mMovementBounds;
     }
@@ -135,28 +134,28 @@
     }
 
     /** Set the normal movement bounds. */
-    public void setNormalMovementBounds(Rect bounds) {
+    public void setNormalMovementBounds(@NonNull Rect bounds) {
         mNormalMovementBounds.set(bounds);
     }
 
     /** Returns the normal movement bounds. */
+    @NonNull
     public Rect getNormalMovementBounds() {
         return mNormalMovementBounds;
     }
 
     /** Set the expanded movement bounds. */
-    public void setExpandedMovementBounds(Rect bounds) {
+    public void setExpandedMovementBounds(@NonNull Rect bounds) {
         mExpandedMovementBounds.set(bounds);
     }
 
     /** Returns the expanded movement bounds. */
+    @NonNull
     public Rect getExpandedMovementBounds() {
         return mExpandedMovementBounds;
     }
 
-    /**
-     * Dictate where PiP currently should be stashed, if at all.
-     */
+    /** Dictate where PiP currently should be stashed, if at all. */
     public void setStashed(@StashType int stashedState) {
         mStashedState = stashedState;
     }
@@ -169,50 +168,39 @@
         return mStashedState;
     }
 
-    /**
-     * Whether PiP is stashed or not.
-     */
+    /** Whether PiP is stashed or not. */
     public boolean isStashed() {
         return mStashedState != STASH_TYPE_NONE;
     }
 
-    /**
-     * Returns the offset from the edge of the screen for PiP stash.
-     */
+    /** Returns the offset from the edge of the screen for PiP stash. */
     public int getStashOffset() {
         return mStashOffset;
     }
 
+    /** Set the PIP aspect ratio. */
     public void setAspectRatio(float aspectRatio) {
         mAspectRatio = aspectRatio;
     }
 
+    /** Get the PIP aspect ratio. */
     public float getAspectRatio() {
         return mAspectRatio;
     }
 
-    /**
-     * 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.
-     */
+    /** Save the reentry state to restore to when re-entering PIP mode. */
     public void saveReentryState(@NonNull Rect bounds, float fraction) {
         mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction);
     }
 
-    /**
-     * Returns the saved reentry state.
-     */
+    /** 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) {
+    /** Set the last {@link ComponentName} to enter PIP mode. */
+    public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) {
         final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
         mLastPipComponentName = lastPipComponentName;
         if (changed) {
@@ -220,41 +208,40 @@
         }
     }
 
+    /** Get the last PIP component name, if any. */
+    @Nullable
     public ComponentName getLastPipComponentName() {
         return mLastPipComponentName;
     }
 
+    /** Get the current display info. */
     @NonNull
     public DisplayInfo getDisplayInfo() {
         return mDisplayInfo;
     }
 
-    /**
-     * Update the display info.
-     */
+    /** Update the display info. */
     public void setDisplayInfo(@NonNull DisplayInfo displayInfo) {
         mDisplayInfo.copyFrom(displayInfo);
     }
 
+    /** Set the rotation of the display. */
     public void setDisplayRotation(int rotation) {
         mDisplayInfo.rotation = rotation;
     }
 
-    /**
-     * Returns the display's bound.
-     */
+    /** Returns the display's bounds. */
     @NonNull
     public Rect getDisplayBounds() {
         return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
     }
 
-    /**
-     * Update the display layout.
-     */
+    /** Update the display layout. */
     public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
         mDisplayLayout.set(displayLayout);
     }
 
+    /** Get the display layout. */
     @NonNull
     public DisplayLayout getDisplayLayout() {
         return mDisplayLayout;
@@ -275,10 +262,8 @@
         return mMinEdgeSize;
     }
 
-    /**
-     * Sets the preferred size of PIP as specified by the activity in PIP mode.
-     */
-    public void setOverrideMinSize(Size overrideMinSize) {
+    /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
+    public void setOverrideMinSize(@Nullable Size overrideMinSize) {
         final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
         mOverrideMinSize = overrideMinSize;
         if (changed && mOnMinimalSizeChangeCallback != null) {
@@ -287,6 +272,7 @@
     }
 
     /** Returns the preferred minimal size specified by the activity in PIP. */
+    @Nullable
     public Size getOverrideMinSize() {
         return mOverrideMinSize;
     }
@@ -297,8 +283,10 @@
         return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
     }
 
-    public AnimatingBoundsState getAnimatingBoundsState() {
-        return mAnimatingBoundsState;
+    /** Get the state of the bounds in motion. */
+    @NonNull
+    public MotionBoundsState getMotionBoundsState() {
+        return mMotionBoundsState;
     }
 
     /** Set whether the IME is currently showing and its height. */
@@ -344,41 +332,41 @@
     /**
      * Registers a callback when the minimal size of PIP that is set by the app changes.
      */
-    public void setOnMinimalSizeChangeCallback(Runnable onMinimalSizeChangeCallback) {
+    public void setOnMinimalSizeChangeCallback(@Nullable Runnable onMinimalSizeChangeCallback) {
         mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback;
     }
 
     /** Set a callback to be notified when the shelf visibility changes. */
     public void setOnShelfVisibilityChangeCallback(
-            BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) {
+            @Nullable BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) {
         mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback;
     }
 
-    /** Source of truth for the current animation bounds of PIP. */
-    public static class AnimatingBoundsState {
-        /** The bounds used when PIP is being dragged or animated. */
-        private final Rect mTemporaryBounds = new Rect();
+    /** Source of truth for the current bounds of PIP that may be in motion. */
+    public static class MotionBoundsState {
+        /** The bounds used when PIP is in motion (e.g. during a drag or animation) */
+        private final @NonNull Rect mBoundsInMotion = new Rect();
         /** The destination bounds to which PIP is animating. */
-        private final Rect mAnimatingToBounds = new Rect();
+        private final @NonNull Rect mAnimatingToBounds = new Rect();
 
         /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */
-        public boolean isAnimating() {
-            return !mTemporaryBounds.isEmpty();
+        public boolean isInMotion() {
+            return !mBoundsInMotion.isEmpty();
         }
 
         /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */
-        public void setTemporaryBounds(Rect bounds) {
-            mTemporaryBounds.set(bounds);
+        public void setBoundsInMotion(@NonNull Rect bounds) {
+            mBoundsInMotion.set(bounds);
         }
 
         /** Set the bounds to which PIP is animating. */
-        public void setAnimatingToBounds(Rect bounds) {
+        public void setAnimatingToBounds(@NonNull Rect bounds) {
             mAnimatingToBounds.set(bounds);
         }
 
-        /** Called when all ongoing dragging and animation operations have ended. */
+        /** Called when all ongoing motion operations have ended. */
         public void onAllAnimationsEnded() {
-            mTemporaryBounds.setEmpty();
+            mBoundsInMotion.setEmpty();
         }
 
         /** Called when an ongoing physics animation has ended. */
@@ -386,20 +374,22 @@
             mAnimatingToBounds.setEmpty();
         }
 
-        /** Returns the temporary animation bounds. */
-        public Rect getTemporaryBounds() {
-            return mTemporaryBounds;
+        /** Returns the motion bounds. */
+        @NonNull
+        public Rect getBoundsInMotion() {
+            return mBoundsInMotion;
         }
 
         /** Returns the destination bounds to which PIP is currently animating. */
+        @NonNull
         public Rect getAnimatingToBounds() {
             return mAnimatingToBounds;
         }
 
         void dump(PrintWriter pw, String prefix) {
             final String innerPrefix = prefix + "  ";
-            pw.println(prefix + AnimatingBoundsState.class.getSimpleName());
-            pw.println(innerPrefix + "mTemporaryBounds=" + mTemporaryBounds);
+            pw.println(prefix + MotionBoundsState.class.getSimpleName());
+            pw.println(innerPrefix + "mBoundsInMotion=" + mBoundsInMotion);
             pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds);
         }
     }
@@ -432,9 +422,7 @@
         }
     }
 
-    /**
-     * Dumps internal state.
-     */
+    /** Dumps internal state. */
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
@@ -461,6 +449,6 @@
         } else {
             mPipReentryState.dump(pw, innerPrefix);
         }
-        mAnimatingBoundsState.dump(pw, innerPrefix);
+        mMotionBoundsState.dump(pw, innerPrefix);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
index 7106075..0528e4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
@@ -20,11 +20,9 @@
 import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
 import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
 
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PointF;
 import android.graphics.Rect;
-import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Calculates the snap targets and the snap position for the PIP given a position and a velocity.
@@ -32,19 +30,6 @@
  */
 public class PipSnapAlgorithm {
 
-    private final float mDefaultSizePercent;
-    private final float mMinAspectRatioForMinSize;
-    private final float mMaxAspectRatioForMinSize;
-
-    public PipSnapAlgorithm(Context context) {
-        Resources res = context.getResources();
-        mDefaultSizePercent = res.getFloat(
-                com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
-        mMaxAspectRatioForMinSize = res.getFloat(
-                com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
-        mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
-    }
-
     /**
      * Returns a fraction that describes where the PiP bounds is.
      * See {@link #getSnapFraction(Rect, Rect, int)}.
@@ -136,81 +121,11 @@
     }
 
     /**
-     * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
-     * {@param stackBounds}.
-     */
-    public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
-            int bottomOffset) {
-        // Adjust the right/bottom to ensure the stack bounds never goes offscreen
-        movementBoundsOut.set(insetBounds);
-        movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right -
-                stackBounds.width());
-        movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom -
-                stackBounds.height());
-        movementBoundsOut.bottom -= bottomOffset;
-    }
-
-    /**
-     * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge
-     * is at least {@param minEdgeSize}.
-     */
-    public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
-            int displayHeight) {
-        final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
-        final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
-
-        final int width;
-        final int height;
-        if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
-            // Beyond these points, we can just use the min size as the shorter edge
-            if (aspectRatio <= 1) {
-                // Portrait, width is the minimum size
-                width = minSize;
-                height = Math.round(width / aspectRatio);
-            } else {
-                // Landscape, height is the minimum size
-                height = minSize;
-                width = Math.round(height * aspectRatio);
-            }
-        } else {
-            // Within these points, we ensure that the bounds fit within the radius of the limits
-            // at the points
-            final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
-            final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
-            height = (int) Math.round(Math.sqrt((radius * radius) /
-                    (aspectRatio * aspectRatio + 1)));
-            width = Math.round(height * aspectRatio);
-        }
-        return new Size(width, height);
-    }
-
-    /**
-     * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
-     * minimum edge is at least minEdgeSize.
-     */
-    public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
-        final int smallestSize = Math.min(size.getWidth(), size.getHeight());
-        final int minSize = (int) Math.max(minEdgeSize, smallestSize);
-
-        final int width;
-        final int height;
-        if (aspectRatio <= 1) {
-            // Portrait, width is the minimum size.
-            width = minSize;
-            height = Math.round(width / aspectRatio);
-        } else {
-            // Landscape, height is the minimum size
-            height = minSize;
-            width = Math.round(height * aspectRatio);
-        }
-        return new Size(width, height);
-    }
-
-    /**
      * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
      * the new bounds out to {@param boundsOut}.
      */
-    public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
+    @VisibleForTesting
+    void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
             @PipBoundsState.StashType int stashType) {
         int leftEdge = stackBounds.left;
         if (stashType == STASH_TYPE_LEFT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 8fa9448..903f7d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -90,7 +90,7 @@
             });
 
     /**
-     * PhysicsAnimator instance for animating {@link PipBoundsState#getAnimatingBoundsState()}
+     * PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
      * using physics animations.
      */
     private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;
@@ -98,7 +98,7 @@
     private MagnetizedObject<Rect> mMagnetizedPip;
 
     /**
-     * Update listener that resizes the PIP to {@link PipBoundsState#getAnimatingBoundsState()}.
+     * Update listener that resizes the PIP to {@link PipBoundsState#getMotionBoundsState()}.
      */
     private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;
 
@@ -172,14 +172,14 @@
         mFloatingContentCoordinator = floatingContentCoordinator;
         mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
         mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
-                mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
+                mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
         mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
                 mSfAnimationHandlerThreadLocal.get());
 
         mResizePipUpdateListener = (target, values) -> {
-            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
+            if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
                 mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
-                        mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), null);
+                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null);
             }
         };
     }
@@ -187,8 +187,8 @@
     @NonNull
     @Override
     public Rect getFloatingBoundsOnScreen() {
-        return !mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds().isEmpty()
-                ? mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds() : getBounds();
+        return !mPipBoundsState.getMotionBoundsState().getAnimatingToBounds().isEmpty()
+                ? mPipBoundsState.getMotionBoundsState().getAnimatingToBounds() : getBounds();
     }
 
     @NonNull
@@ -207,7 +207,7 @@
      */
     void synchronizePinnedStackBounds() {
         cancelPhysicsAnimation();
-        mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
+        mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
 
         if (mPipTaskOrganizer.isInPip()) {
             mFloatingContentCoordinator.onContentMoved(this);
@@ -242,7 +242,7 @@
                 resizePipUnchecked(toBounds);
                 mPipBoundsState.setBounds(toBounds);
             } else {
-                mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(toBounds);
+                mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds);
                 mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
                         (Rect newBounds) -> {
                             mMainHandler.post(() -> {
@@ -278,8 +278,8 @@
 
         // If we're already in the dismiss target area, then there won't be a move to set the
         // temporary bounds, so just initialize it to the current bounds.
-        if (!mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
-            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
+        if (!mPipBoundsState.getMotionBoundsState().isInMotion()) {
+            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds());
         }
         mTemporaryBoundsPhysicsAnimator
                 .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
@@ -396,7 +396,7 @@
 
         final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
 
-        final int startValueY = mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds().top;
+        final int startValueY = mPipBoundsState.getMotionBoundsState().getBoundsInMotion().top;
         final float estimatedFlingYEndValue =
                 PhysicsAnimator.estimateFlingEndValue(startValueY, velocityY, mFlingConfigY);
 
@@ -411,7 +411,7 @@
     void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
         if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
             // Animate from the current bounds if we're not already animating.
-            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
+            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds());
         }
 
         mTemporaryBoundsPhysicsAnimator
@@ -492,7 +492,7 @@
      */
     private void cancelPhysicsAnimation() {
         mTemporaryBoundsPhysicsAnimator.cancel();
-        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
+        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
         mSpringingToTouch = false;
     }
 
@@ -565,17 +565,17 @@
         if (!mDismissalPending
                 && !mSpringingToTouch
                 && !mMagnetizedPip.getObjectStuckToTarget()) {
-            // All animations (including dragging) have actually finished.
+            // All motion operations have actually finished.
             mPipBoundsState.setBounds(
-                    mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
-            mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
+                    mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
             if (!mDismissalPending) {
                 // do not schedule resize if PiP is dismissing, which may cause app re-open to
                 // mBounds instead of it's normal bounds.
                 mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
             }
         }
-        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
+        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
         mSpringingToTouch = false;
         mDismissalPending = false;
     }
@@ -586,7 +586,7 @@
      * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
      */
     private void setAnimatingToBounds(Rect bounds) {
-        mPipBoundsState.getAnimatingBoundsState().setAnimatingToBounds(bounds);
+        mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(bounds);
         mFloatingContentCoordinator.onContentMoved(this);
     }
 
@@ -625,7 +625,7 @@
     MagnetizedObject<Rect> getMagnetizedPip() {
         if (mMagnetizedPip == null) {
             mMagnetizedPip = new MagnetizedObject<Rect>(
-                    mContext, mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(),
+                    mContext, mPipBoundsState.getMotionBoundsState().getBoundsInMotion(),
                     FloatProperties.RECT_X, FloatProperties.RECT_Y) {
                 @Override
                 public float getWidth(@NonNull Rect animatedPipBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 1c5d5b8..a78c4ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -307,8 +307,7 @@
 
     public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
         final Rect toMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
-                toMovementBounds, 0);
+        mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
         final int prevBottom = mPipBoundsState.getMovementBounds().bottom
                 - mMovementBoundsExtraOffsets;
         if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
@@ -339,13 +338,13 @@
 
         // Re-calculate the expanded bounds
         Rect normalMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds,
+        mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds,
                 normalMovementBounds, bottomOffset);
 
         if (mPipBoundsState.getMovementBounds().isEmpty()) {
             // mMovementBounds is not initialized yet and a clean movement bounds without
             // bottom offset shall be used later in this function.
-            mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+            mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
                     mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */);
         }
 
@@ -353,12 +352,12 @@
         float aspectRatio = (float) normalBounds.width() / normalBounds.height();
         Point displaySize = new Point();
         mContext.getDisplay().getRealSize(displaySize);
-        Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio(
+        Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
                 aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
         mPipBoundsState.setExpandedBounds(
                 new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
         Rect expandedMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(
+        mPipBoundsAlgorithm.getMovementBounds(
                 mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
                 bottomOffset);
 
@@ -381,7 +380,7 @@
             } else {
                 final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
                 final Rect toMovementBounds = new Rect();
-                mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+                mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
                         toMovementBounds, mIsImeShowing ? mImeHeight : 0);
                 final int prevBottom = mPipBoundsState.getMovementBounds().bottom
                         - mMovementBoundsExtraOffsets;
@@ -659,7 +658,7 @@
 
     private void animateToUnexpandedState(Rect restoreBounds) {
         Rect restoredMovementBounds = new Rect();
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds,
+        mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
                 mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
         mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
                 restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */);
@@ -707,7 +706,7 @@
                 return;
             }
 
-            Rect bounds = getPossiblyAnimatingBounds();
+            Rect bounds = getPossiblyMotionBounds();
             mDelta.set(0f, 0f);
             mStartPosition.set(bounds.left, bounds.top);
             mMovementWithinDismiss = touchState.getDownTouchPosition().y
@@ -746,7 +745,7 @@
                 mDelta.x += left - lastX;
                 mDelta.y += top - lastY;
 
-                mTmpBounds.set(getPossiblyAnimatingBounds());
+                mTmpBounds.set(getPossiblyMotionBounds());
                 mTmpBounds.offsetTo((int) left, (int) top);
                 mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
 
@@ -865,7 +864,7 @@
      * resized.
      */
     private void updateMovementBounds() {
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mPipBoundsState.getBounds(),
+        mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
                 mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
         mMotionHelper.onMovementBoundsChanged();
 
@@ -877,7 +876,7 @@
 
     private Rect getMovementBounds(Rect curBounds) {
         Rect movementBounds = new Rect();
-        mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds,
+        mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds,
                 movementBounds, mIsImeShowing ? mImeHeight : 0);
         return movementBounds;
     }
@@ -896,12 +895,12 @@
     }
 
     /**
-     * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
-     * otherwise.
+     * Returns the PIP bounds if we're not in the middle of a motion operation, or the current,
+     * temporary motion bounds otherwise.
      */
-    Rect getPossiblyAnimatingBounds() {
-        return mPipBoundsState.getAnimatingBoundsState().isAnimating()
-                ? mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()
+    Rect getPossiblyMotionBounds() {
+        return mPipBoundsState.getMotionBoundsState().isInMotion()
+                ? mPipBoundsState.getMotionBoundsState().getBoundsInMotion()
                 : mPipBoundsState.getBounds();
     }
 
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
new file mode 100644
index 0000000..2c6c7b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -0,0 +1,2 @@
+# includes OWNERS from parent directories
+natanieljr@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index e1c9384..0fb43e2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -92,6 +92,26 @@
 }
 
 @JvmOverloads
+fun LayersAssertion.appPairsDividerIsVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    end("appPairsDividerIsVisible", bugId, enabled) {
+        this.showsLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.appPairsDividerIsInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    end("appPairsDividerIsInVisible", bugId, enabled) {
+        this.hasNotLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+    }
+}
+
+@JvmOverloads
 fun LayersAssertion.dockedStackDividerIsVisible(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 2e6037d..54b8fdc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -130,6 +130,7 @@
         const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
         const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
         const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+        const val APP_PAIRS_DIVIDER = "AppPairDivider"
         const val IMAGE_WALLPAPER = "ImageWallpaper"
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
new file mode 100644
index 0000000..2fc6944
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -0,0 +1,223 @@
+/*
+ * 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.flicker.apppairs
+
+import android.os.SystemClock
+import android.util.Log
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS
+import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+import java.io.IOException
+
+/**
+ * Test AppPairs launch.
+ * To run this test: `atest WMShellFlickerTests:AppPairsTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTest(
+    rotationName: String,
+    rotation: Int
+) : AppPairsTestBase(rotationName, rotation) {
+    private val appPairsSetup: FlickerBuilder
+        get() = FlickerBuilder(instrumentation).apply {
+            val testLaunchActivity = "launch_appPairs_primary_secondary_activities"
+            withTestName {
+                testLaunchActivity
+            }
+            setup {
+                eachRun {
+                    uiDevice.wakeUpAndGoToHomeScreen()
+                    primaryApp.open()
+                    uiDevice.pressHome()
+                    secondaryApp.open()
+                    uiDevice.pressHome()
+                    updateTaskId()
+                }
+            }
+            teardown {
+                eachRun {
+                    executeShellCommand(composePairsCommand(
+                            primaryTaskId, secondaryTaskId, false /* pair */))
+                    primaryApp.exit()
+                    secondaryApp.exit()
+                }
+            }
+            assertions {
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                }
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                }
+            }
+        }
+
+    @Test
+    fun testAppPairs_pairPrimaryAndSecondaryApps() {
+        val testTag = "testAppPaired_pairPrimaryAndSecondary"
+        runWithFlicker(appPairsSetup) {
+            withTestName { testTag }
+            repeat {
+                TEST_REPETITIONS
+            }
+            transitions {
+                // TODO pair apps through normal UX flow
+                executeShellCommand(composePairsCommand(
+                        primaryTaskId, secondaryTaskId, true /* pair */))
+                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+            }
+            assertions {
+                layersTrace {
+                    appPairsDividerIsVisible()
+                    end("appsEndingBounds", enabled = false) {
+                        val entry = this.trace.entries.firstOrNull()
+                                ?: throw IllegalStateException("Trace is empty")
+                        val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+                        this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                appPairsHelper.getPrimaryBounds(dividerRegion))
+                                .and()
+                                .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                    }
+                }
+                windowManagerTrace {
+                    end {
+                        showsAppWindow(primaryApp.defaultWindowName)
+                                .and()
+                                .showsAppWindow(secondaryApp.defaultWindowName)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testAppPairs_unpairPrimaryAndSecondary() {
+        val testTag = "testAppPairs_unpairPrimaryAndSecondary"
+        runWithFlicker(appPairsSetup) {
+            withTestName { testTag }
+            repeat {
+                TEST_REPETITIONS
+            }
+            setup {
+                executeShellCommand(composePairsCommand(
+                        primaryTaskId, secondaryTaskId, true /* pair */))
+                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+            }
+            transitions {
+                // TODO pair apps through normal UX flow
+                executeShellCommand(composePairsCommand(
+                        primaryTaskId, secondaryTaskId, false /* pair */))
+                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+            }
+            assertions {
+                layersTrace {
+                    appPairsDividerIsInvisible()
+                    start("appsStartingBounds", enabled = false) {
+                        val entry = this.trace.entries.firstOrNull()
+                                ?: throw IllegalStateException("Trace is empty")
+                        val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+                        this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                appPairsHelper.getPrimaryBounds(dividerRegion))
+                                .and()
+                                .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                    }
+                    end("appsEndingBounds", enabled = false) {
+                        this.hasNotLayer(primaryApp.defaultWindowName)
+                                .and()
+                                .hasNotLayer(secondaryApp.defaultWindowName)
+                    }
+                }
+                windowManagerTrace {
+                    end {
+                        hidesAppWindow(primaryApp.defaultWindowName)
+                                .and()
+                                .hidesAppWindow(secondaryApp.defaultWindowName)
+                    }
+                }
+            }
+        }
+    }
+
+    private fun composePairsCommand(
+        primaryApp: String,
+        secondaryApp: String,
+        pair: Boolean
+    ): String = buildString {
+        // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2}
+        append("dumpsys activity service SystemUIService WMShell ")
+        if (pair) {
+            append("pair ")
+        } else {
+            append("unpair ")
+        }
+        append(primaryApp + " " + secondaryApp)
+    }
+
+    private fun executeShellCommand(cmd: String) {
+        try {
+            SystemUtil.runShellCommand(instrumentation, cmd)
+        } catch (e: IOException) {
+            Log.d("AppPairsTest", "executeShellCommand error!" + e)
+        }
+    }
+
+    private fun updateTaskId() {
+        val primaryAppComponent = primaryApp.openAppIntent.component
+        val secondaryAppComponent = secondaryApp.openAppIntent.component
+        if (primaryAppComponent != null) {
+            primaryTaskId = appPairsHelper.getTaskIdForActivity(
+                    primaryAppComponent.packageName, primaryAppComponent.className).toString()
+        }
+        if (secondaryAppComponent != null) {
+            secondaryTaskId = appPairsHelper.getTaskIdForActivity(
+                    secondaryAppComponent.packageName, secondaryAppComponent.className).toString()
+        }
+    }
+
+    companion object {
+        var primaryTaskId = ""
+        var secondaryTaskId = ""
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val supportedRotations = intArrayOf(Surface.ROTATION_0)
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
new file mode 100644
index 0000000..1a4de0a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.flicker.apppairs
+
+import com.android.wm.shell.flicker.NonRotationTestBase
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class AppPairsTestBase(
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    protected val appPairsHelper = AppPairsHelper(instrumentation,
+            TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+            TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+    protected val primaryApp = SplitScreenHelper(instrumentation,
+            TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+            TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+    protected val secondaryApp = SplitScreenHelper(instrumentation,
+            TEST_APP_SPLITSCREEN_SECONDARY_LABEL,
+            TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
new file mode 100644
index 0000000..3b6fcdb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.graphics.Region
+import android.system.helpers.ActivityHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+class AppPairsHelper(
+    instrumentation: Instrumentation,
+    activityLabel: String,
+    componentName: ComponentName
+) : BaseAppHelper(
+    instrumentation,
+    activityLabel,
+    componentName
+) {
+    val activityHelper = ActivityHelper.getInstance()
+
+    fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+        val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+                dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
+        return primaryAppBounds
+    }
+
+    fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+        val displayBounds = WindowUtils.displayBounds
+        val secondaryAppBounds = Region(0,
+                dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
+                displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+        return secondaryAppBounds
+    }
+
+    fun getTaskIdForActivity(pkgName: String, activityName: String): Int {
+        return activityHelper.getTaskIdForActivity(pkgName, activityName)
+    }
+
+    companion object {
+        const val TEST_REPETITIONS = 1
+        const val TIMEOUT_MS = 3_000L
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 6f008ce..22496a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.helpers
 
-import android.app.ActivityManager
 import android.app.Instrumentation
 import android.content.ComponentName
 import android.content.Context
@@ -46,9 +45,6 @@
     protected val context: Context
         get() = mInstrumentation.context
 
-    private val activityManager: ActivityManager?
-        get() = context.getSystemService(ActivityManager::class.java)
-
     private val appSelector = By.pkg(packageName).depth(0)
 
     protected val isTelevision: Boolean
@@ -77,10 +73,6 @@
         return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
     }
 
-    fun forceStop() {
-        activityManager?.forceStopPackage(packageName)
-    }
-
     override fun getOpenAppIntent(): Intent {
         val intent = Intent()
         intent.component = launcherActivityComponent
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index d343f2a..abb8fc5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -210,4 +210,4 @@
             return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index d1906ba..251bc06 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -53,7 +53,7 @@
     open fun tearDown() {
         if (!isTelevision) return
 
-        testApp.forceStop()
+        testApp.exit()
 
         // Wait for 1 second, and check if the SystemUI has been alive and well since the start.
         SystemClock.sleep(AFTER_TEXT_PROCESS_CHECK_DELAY)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
index 6e10f93..a0056df 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
@@ -60,8 +60,8 @@
             }
             teardown {
                 eachRun {
-                    splitScreenApp.forceStop()
-                    secondaryApp.forceStop()
+                    splitScreenApp.exit()
+                    secondaryApp.exit()
                 }
             }
             assertions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
index 17fc862..32e112d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
@@ -65,8 +65,8 @@
             }
             teardown {
                 eachRun {
-                    splitScreenApp.forceStop()!!
-                    secondaryApp.forceStop()!!
+                    splitScreenApp.exit()
+                    secondaryApp.exit()
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
index 9ab0f89..754f732 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -16,15 +16,25 @@
 
 package com.android.wm.shell.apppairs;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.ActivityManager;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.hardware.display.DisplayManager;
+
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -36,22 +46,32 @@
 /** Tests for {@link AppPair} */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class AppPairTests {
+public class AppPairTests extends ShellTestCase {
 
     private AppPairsController mController;
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
+    @Mock private DisplayController mDisplayController;
+    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+        mController = new TestAppPairsController(
+                mTaskOrganizer,
+                mSyncQueue,
+                mDisplayController,
+                mTaskStackListener);
+        when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+        when(mDisplayController.getDisplay(anyInt())).thenReturn(
+                mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
     }
 
     @After
     public void tearDown() {}
 
     @Test
+    @UiThreadTest
     public void testContains() {
         final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
         final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
@@ -66,6 +86,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testVanishUnpairs() {
         final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
         final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
index ed85b67..6d441ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
@@ -16,15 +16,25 @@
 
 package com.android.wm.shell.apppairs;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.ActivityManager;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.hardware.display.DisplayManager;
+
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -36,23 +46,33 @@
 /** Tests for {@link AppPairsController} */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class AppPairsControllerTests {
+public class AppPairsControllerTests extends ShellTestCase {
     private TestAppPairsController mController;
     private TestAppPairsPool mPool;
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
+    @Mock private DisplayController mDisplayController;
+    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+        mController = new TestAppPairsController(
+                mTaskOrganizer,
+                mSyncQueue,
+                mDisplayController,
+                mTaskStackListener);
         mPool = mController.getPool();
+        when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+        when(mDisplayController.getDisplay(anyInt())).thenReturn(
+                mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
     }
 
     @After
     public void tearDown() {}
 
     @Test
+    @UiThreadTest
     public void testPairUnpair() {
         final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
         final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
@@ -69,6 +89,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testUnpair_DontReleaseToPool() {
         final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
         final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
index 4a0fe0f..d3dbbfe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
@@ -22,7 +22,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.After;
 import org.junit.Before;
@@ -39,11 +41,17 @@
     private TestAppPairsPool mPool;
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
+    @Mock private DisplayController mDisplayController;
+    @Mock private TaskStackListenerImpl mTaskStackListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+        mController = new TestAppPairsController(
+                mTaskOrganizer,
+                mSyncQueue,
+                mDisplayController,
+                mTaskStackListener);
         mPool = mController.getPool();
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
index 7ea5a1b..e61cc91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -17,13 +17,16 @@
 package com.android.wm.shell.apppairs;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 
 public class TestAppPairsController extends AppPairsController {
     TestAppPairsPool mPool;
 
-    public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
-        super(organizer, syncQueue);
+    public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+            DisplayController displayController, TaskStackListenerImpl taskStackListener) {
+        super(organizer, syncQueue, displayController, taskStackListener);
         mPool = new TestAppPairsPool(this);
         setPairsPool(mPool);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 7a6e0c1..a65d832 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -50,6 +50,7 @@
     private static final float DEFAULT_ASPECT_RATIO = 1f;
     private static final float MIN_ASPECT_RATIO = 0.5f;
     private static final float MAX_ASPECT_RATIO = 2f;
+    private static final int DEFAULT_MIN_EDGE_SIZE = 100;
 
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private DisplayInfo mDefaultDisplayInfo;
@@ -73,7 +74,8 @@
                 com.android.internal.R.integer.config_defaultPictureInPictureGravity,
                 Gravity.END | Gravity.BOTTOM);
         res.addOverride(
-                com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100);
+                com.android.internal.R.dimen.default_minimal_size_pip_resizable_task,
+                DEFAULT_MIN_EDGE_SIZE);
         res.addOverride(
                 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
                 "16x16");
@@ -112,6 +114,127 @@
     }
 
     @Test
+    public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
+        final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
+                DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
+                mDefaultDisplayInfo.logicalHeight);
+
+        mPipBoundsState.setOverrideMinSize(null);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height()));
+        assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+                ASPECT_RATIO_ERROR_MARGIN);
+    }
+
+    @Test
+    public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() {
+        overrideDefaultAspectRatio(1.0f);
+        // The min size's aspect ratio is greater than the default aspect ratio.
+        final Size overrideMinSize = new Size(150, 120);
+
+        mPipBoundsState.setOverrideMinSize(overrideMinSize);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        // The default aspect ratio should trump the min size aspect ratio.
+        assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+                ASPECT_RATIO_ERROR_MARGIN);
+        // The width of the min size is still used with the default aspect ratio.
+        assertEquals(overrideMinSize.getWidth(), defaultBounds.width());
+    }
+
+    @Test
+    public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() {
+        overrideDefaultAspectRatio(1.0f);
+        // The min size's aspect ratio is greater than the default aspect ratio.
+        final Size overrideMinSize = new Size(120, 150);
+
+        mPipBoundsState.setOverrideMinSize(overrideMinSize);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        // The default aspect ratio should trump the min size aspect ratio.
+        assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+                ASPECT_RATIO_ERROR_MARGIN);
+        // The height of the min size is still used with the default aspect ratio.
+        assertEquals(overrideMinSize.getHeight(), defaultBounds.height());
+    }
+
+    @Test
+    public void getDefaultBounds_imeShowing_offsetByImeHeight() {
+        final int imeHeight = 30;
+        mPipBoundsState.setImeVisibility(false, 0);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        mPipBoundsState.setImeVisibility(true, imeHeight);
+        final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+        assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top);
+    }
+
+    @Test
+    public void getDefaultBounds_shelfShowing_offsetByShelfHeight() {
+        final int shelfHeight = 30;
+        mPipBoundsState.setShelfVisibility(false, 0);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        mPipBoundsState.setShelfVisibility(true, shelfHeight);
+        final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds();
+
+        assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top);
+    }
+
+    @Test
+    public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() {
+        final int imeHeight = 30;
+        final int shelfHeight = 40;
+        mPipBoundsState.setImeVisibility(false, 0);
+        mPipBoundsState.setShelfVisibility(false, 0);
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        mPipBoundsState.setImeVisibility(true, imeHeight);
+        mPipBoundsState.setShelfVisibility(true, shelfHeight);
+        final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+        assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top);
+    }
+
+    @Test
+    public void getDefaultBounds_boundsAtDefaultGravity() {
+        final Rect insetBounds = new Rect();
+        mPipBoundsAlgorithm.getInsetBounds(insetBounds);
+        overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM);
+
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        assertEquals(insetBounds.bottom, defaultBounds.bottom);
+        assertEquals(insetBounds.right, defaultBounds.right);
+    }
+
+    @Test
+    public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() {
+        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+        // Set an invalid current aspect ratio.
+        mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2);
+        final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+        assertEquals(defaultBounds, normalBounds);
+    }
+
+    @Test
+    public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() {
+        final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds();
+        mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio,
+                MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
+
+        // Set a valid current aspect ratio different that the default.
+        mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO);
+        final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+        assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds);
+    }
+
+    @Test
     public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() {
         final float[] aspectRatios = new float[] {
                 (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
@@ -121,8 +244,7 @@
         for (float aspectRatio : aspectRatios) {
             mPipBoundsState.setAspectRatio(aspectRatio);
             final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
-            final float actualAspectRatio =
-                    destinationBounds.width() / (destinationBounds.height() * 1f);
+            final float actualAspectRatio = getRectAspectRatio(destinationBounds);
             assertEquals("Destination bounds matches the given aspect ratio",
                     aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
         }
@@ -274,6 +396,22 @@
         assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
     }
 
+    private void overrideDefaultAspectRatio(float aspectRatio) {
+        final TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+                aspectRatio);
+        mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+    }
+
+    private void overrideDefaultStackGravity(int stackGravity) {
+        final TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+                stackGravity);
+        mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+    }
+
     private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
         final Rect expectedWithMargin = new Rect(expected);
         expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
@@ -282,4 +420,8 @@
                 + " with error margin " + ROUNDING_ERROR_MARGIN,
                 expectedWithMargin.contains(actual));
     }
+
+    private static float getRectAspectRatio(Rect rect) {
+        return rect.width() / (rect.height() * 1f);
+    }
 }
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
index 59e10c1..4bcca06 100644
--- 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
@@ -135,4 +135,38 @@
 
         verify(callback, never()).accept(true, 100);
     }
+
+    @Test
+    public void testSetOverrideMinSize_changed_callbackInvoked() {
+        final Runnable callback = mock(Runnable.class);
+        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
+        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);
+
+        mPipBoundsState.setOverrideMinSize(new Size(10, 10));
+
+        verify(callback).run();
+    }
+
+    @Test
+    public void testSetOverrideMinSize_notChanged_callbackNotInvoked() {
+        final Runnable callback = mock(Runnable.class);
+        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
+        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);
+
+        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
+
+        verify(callback, never()).run();
+    }
+
+    @Test
+    public void testGetOverrideMinEdgeSize() {
+        mPipBoundsState.setOverrideMinSize(null);
+        assertEquals(0, mPipBoundsState.getOverrideMinEdgeSize());
+
+        mPipBoundsState.setOverrideMinSize(new Size(5, 10));
+        assertEquals(5, mPipBoundsState.getOverrideMinEdgeSize());
+
+        mPipBoundsState.setOverrideMinSize(new Size(15, 10));
+        assertEquals(10, mPipBoundsState.getOverrideMinEdgeSize());
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
new file mode 100644
index 0000000..dcee2e1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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 android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link PipSnapAlgorithm}. **/
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipSnapAlgorithmTest extends ShellTestCase {
+    private static final int DEFAULT_STASH_OFFSET = 32;
+    private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000);
+    private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100);
+    private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0,
+            DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(),
+            DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width());
+
+    private PipSnapAlgorithm mPipSnapAlgorithm;
+
+    @Before
+    public void setUp() {
+        mPipSnapAlgorithm = new PipSnapAlgorithm();
+    }
+
+    @Test
+    public void testApplySnapFraction_topEdge() {
+        final float snapFraction = 0.25f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+        assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_rightEdge() {
+        final float snapFraction = 1.5f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+        assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_bottomEdge() {
+        final float snapFraction = 2.25f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+        assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_leftEdge() {
+        final float snapFraction = 3.75f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+        assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+        assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_notStashed_isNotOffBounds() {
+        final float snapFraction = 2f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+                PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+        assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_stashedLeft() {
+        final float snapFraction = 3f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+                PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+        final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET;
+        assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+    }
+
+    @Test
+    public void testApplySnapFraction_stashedRight() {
+        final float snapFraction = 2f;
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+                PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+        assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left);
+        assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+    }
+
+    @Test
+    public void testSnapRectToClosestEdge_rightEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move the centered rect slightly to the right side.
+        bounds.offset(10, 0);
+
+        mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+                PipBoundsState.STASH_TYPE_NONE);
+
+        assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+    }
+
+    @Test
+    public void testSnapRectToClosestEdge_leftEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move the centered rect slightly to the left side.
+        bounds.offset(-10, 0);
+
+        mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+                PipBoundsState.STASH_TYPE_NONE);
+
+        assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+    }
+
+    @Test
+    public void testSnapRectToClosestEdge_topEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move the centered rect slightly to the top half.
+        bounds.offset(0, -10);
+
+        mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+                PipBoundsState.STASH_TYPE_NONE);
+
+        assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+    }
+
+    @Test
+    public void testSnapRectToClosestEdge_bottomEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move the centered rect slightly to the bottom half.
+        bounds.offset(0, 10);
+
+        mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+                PipBoundsState.STASH_TYPE_NONE);
+
+        assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+    }
+
+    @Test
+    public void testSnapRectToClosestEdge_stashed_unStahesBounds() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Stash it on the left side.
+        mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f,
+                PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+        mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+                PipBoundsState.STASH_TYPE_LEFT);
+
+        assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+    }
+
+    @Test
+    public void testGetSnapFraction_leftEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move it slightly to the left side.
+        bounds.offset(-10, 0);
+
+        final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+        assertEquals(3.5f, snapFraction, 0.1f);
+    }
+
+    @Test
+    public void testGetSnapFraction_rightEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move it slightly to the right side.
+        bounds.offset(10, 0);
+
+        final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+        assertEquals(1.5f, snapFraction, 0.1f);
+    }
+
+    @Test
+    public void testGetSnapFraction_topEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move it slightly to the top half.
+        bounds.offset(0, -10);
+
+        final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+        assertEquals(0.5f, snapFraction, 0.1f);
+    }
+
+    @Test
+    public void testGetSnapFraction_bottomEdge() {
+        final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+        // Move it slightly to the bottom half.
+        bounds.offset(0, 10);
+
+        final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+        assertEquals(2.5f, snapFraction, 0.1f);
+    }
+}
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 b25c74d..abbc681 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
@@ -91,7 +91,7 @@
         mPipBoundsState = new PipBoundsState(mContext);
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
         mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
-        mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+        mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
                 mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
                 mFloatingContentCoordinator, mPipUiEventLogger);
@@ -115,7 +115,8 @@
     @Test
     public void updateMovementBounds_minBounds() {
         Rect expectedMinMovementBounds = new Rect();
-        mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+        mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds,
+                0);
 
         mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
                 mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
@@ -129,12 +130,13 @@
     public void updateMovementBounds_maxBounds() {
         Point displaySize = new Point();
         mContext.getDisplay().getRealSize(displaySize);
-        Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+        Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1,
                 mContext.getResources().getDimensionPixelSize(
                         R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
         Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
         Rect expectedMaxMovementBounds = new Rect();
-        mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+        mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds,
+                0);
 
         mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
                 mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8c2a632..a545b3d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1041,7 +1041,9 @@
 
 base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
   std::vector<uint32_t> found_resids;
-  return GetBag(resid, found_resids);
+  const auto bag = GetBag(resid, found_resids);
+  cached_bag_resid_stacks_.emplace(resid, found_resids);
+  return bag;
 }
 
 base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index b88ffa6..a2964d6 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -94,13 +94,27 @@
 }
 
 static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
-        int weight, int italic) {
+                                      jlong fallbackPtr, int weight, int italic) {
     ScopedLongArrayRO families(env, familyArray);
     std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
-    familyVec.reserve(families.size());
-    for (size_t i = 0; i < families.size(); i++) {
-        FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
-        familyVec.emplace_back(family->family);
+    Typeface* typeface = (fallbackPtr == 0) ? nullptr : toTypeface(fallbackPtr);
+    if (typeface != nullptr) {
+        const std::vector<std::shared_ptr<minikin::FontFamily>>& fallbackFamilies =
+            toTypeface(fallbackPtr)->fFontCollection->getFamilies();
+        familyVec.reserve(families.size() + fallbackFamilies.size());
+        for (size_t i = 0; i < families.size(); i++) {
+            FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
+            familyVec.emplace_back(family->family);
+        }
+        for (size_t i = 0; i < fallbackFamilies.size(); i++) {
+            familyVec.emplace_back(fallbackFamilies[i]);
+        }
+    } else {
+        familyVec.reserve(families.size());
+        for (size_t i = 0; i < families.size(); i++) {
+            FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
+            familyVec.emplace_back(family->family);
+        }
     }
     return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic));
 }
@@ -145,6 +159,13 @@
     return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> {
         std::string path(fontPath.data(), fontPath.size());
         sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
+        if (data == nullptr) {
+            // This may happen if:
+            // 1. When the process failed to open the file (e.g. invalid path or permission).
+            // 2. When the process failed to map the file (e.g. hitting max_map_count limit).
+            ALOGE("Failed to make SkData from file name: %s", path.c_str());
+            return nullptr;
+        }
         const void* fontPtr = data->data();
         size_t fontSize = data->size();
         std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);
@@ -243,7 +264,7 @@
     { "nativeGetReleaseFunc",     "()J",  (void*)Typeface_getReleaseFunc },
     { "nativeGetStyle",           "(J)I",  (void*)Typeface_getStyle },
     { "nativeGetWeight",      "(J)I",  (void*)Typeface_getWeight },
-    { "nativeCreateFromArray",    "([JII)J",
+    { "nativeCreateFromArray",    "([JJII)J",
                                            (void*)Typeface_createFromArray },
     { "nativeSetDefault",         "(J)V",   (void*)Typeface_setDefault },
     { "nativeGetSupportedAxes",   "(J)[I",  (void*)Typeface_getSupportedAxes },
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index e023aa1..9dbdede 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -150,8 +150,8 @@
         return mListenerId;
     }
 
-    /** Returns true if this represents a system identity. */
-    public boolean isSystem() {
+    /** Returns true if this represents a system server identity. */
+    public boolean isSystemServer() {
         return mUid == Process.SYSTEM_UID;
     }
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 973c2a8..4b11e32 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -4237,14 +4237,14 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission("android.permission.BIND_IMS_SERVICE")
+    @RequiresPermission(BIND_IMS_SERVICE)
     public void setOnRtpRxNoticeListener(
             @NonNull Context context,
             @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) {
         Objects.requireNonNull(context);
         Preconditions.checkArgument(
                 context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED,
-                "android.permission.BIND_IMS_SERVICE permission not granted.");
+                BIND_IMS_SERVICE + " permission not granted.");
         mOnRtpRxNoticeListener = Objects.requireNonNull(listener);
         mOnRtpRxNoticeHandler = handler;
     }
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 5f79dc5..2217eb3 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemApi;
 import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.filter.Filter;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -115,9 +116,9 @@
      * keys for different purposes.
      *
      * @param keyToken the token to be used to link the key slot. Use {@link Tuner.INVALID_KEYTOKEN}
-     *        to remove the to remove the current key from descrambler. If the current keyToken
-     *        comes from MediaCas session, use {@link Tuner.INVALID_KEYTOKEN} to remove current key
-     *        before close MediaCas session.
+     *        to remove the current key from descrambler. If the current keyToken comes from a
+     *        MediaCas session, use {@link Tuner.INVALID_KEYTOKEN} to remove current key before
+     *        closing the MediaCas session.
      * @return result status of the operation.
      */
     @Result
@@ -125,6 +126,9 @@
         synchronized (mLock) {
             TunerUtils.checkResourceState(TAG, mIsClosed);
             Objects.requireNonNull(keyToken, "key token must not be null");
+            if (!isValidKeyToken(keyToken)) {
+                return Tuner.RESULT_INVALID_ARGUMENT;
+            }
             return nativeSetKeyToken(keyToken);
         }
     }
@@ -147,4 +151,17 @@
         }
     }
 
+    private boolean isValidKeyToken(byte[] keyToken) {
+        if (keyToken.length == 0 || keyToken.length > 16) {
+            Log.d(TAG, "Invalid key token size: " + (keyToken.length * 8) + " bit.");
+            return false;
+        }
+        for (int i = 0; i < keyToken.length; i++) {
+            if (keyToken[i] < 0) {
+                Log.d(TAG, "Invalid key token.");
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 27b33ac..a9da772 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -878,11 +878,14 @@
     }
 
     /**
-     * Connects Conditional Access Modules (CAM) through Common Interface (CI)
+     * Connects Conditional Access Modules (CAM) through Common Interface (CI).
      *
      * <p>The demux uses the output from the frontend as the input by default, and must change to
      * use the output from CI-CAM as the input after this call.
      *
+     * <p> Note that this API is used to connect the CI-CAM to the Demux module while
+     * {@link connectFrontendToCiCam(int)} is used to connect CI-CAM to the Frontend module.
+     *
      * @param ciCamId specify CI-CAM Id to connect.
      * @return result status of the operation.
      */
@@ -895,23 +898,30 @@
     }
 
     /**
-     * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode.
+     * Connect Conditional Access Modules (CAM) Frontend to support Common Interface (CI)
+     * by-pass mode.
      *
      * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that
      * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving
      * the TS directly from the frontend.
      *
-     * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect.
+     * <p> Note that this API is used to connect the CI-CAM to the Frontend module while
+     * {@link connectCiCam(int)} is used to connect CI-CAM to the Demux module.
+     *
+     * <p>Use {@link #disconnectFrontendToCiCam(int)} to disconnect.
      *
      * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
      * no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to
      * check the version.
      *
-     * @param ciCamId specify CI-CAM Id to link.
+     * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
+     *                Common Interface (CI), to link.
      * @return Local transport stream id when connection is successfully established. Failed
-     *         operation returns {@link INVALID_LTS_ID}.
+     *         operation returns {@link INVALID_LTS_ID} while unsupported version also returns
+     *         {@link INVALID_LTS_ID}. Check the current HAL version using
+     *         {@link TunerVersionChecker.getTunerVersion()}.
      */
-    public int linkFrontendToCiCam(int ciCamId) {
+    public int connectFrontendToCiCam(int ciCamId) {
         if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
                 "linkFrontendToCiCam")) {
             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
@@ -922,10 +932,13 @@
     }
 
     /**
-     * Disconnects Conditional Access Modules (CAM)
+     * Disconnects Conditional Access Modules (CAM).
      *
      * <p>The demux will use the output from the frontend as the input after this call.
      *
+     * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
+     * {@link disconnectFrontendToCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
+     *
      * @return result status of the operation.
      */
     @Result
@@ -937,18 +950,23 @@
     }
 
     /**
-     * Unlink Conditional Access Modules (CAM) Frontend.
+     * Disconnect Conditional Access Modules (CAM) Frontend.
      *
      * <p>It is used by the client to unlink CI-CAM to a Frontend.
      *
+     * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
+     * {@link disconnectCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
+     *
      * <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.
      *
-     * @param ciCamId specify CI-CAM Id to unlink.
-     * @return result status of the operation.
+     * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
+     *                Common Interface (CI), to disconnect.
+     * @return result status of the operation. Unsupported version would return
+     *         {@link RESULT_UNAVAILABLE}
      */
     @Result
-    public int unlinkFrontendToCiCam(int ciCamId) {
+    public int disconnectFrontendToCiCam(int ciCamId) {
         if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
                 "unlinkFrontendToCiCam")) {
             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 82cc78d..451f54c 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -186,7 +186,7 @@
             value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED,
                     SCRAMBLING_STATUS_SCRAMBLED})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ScramblingStatusMask {}
+    public @interface ScramblingStatus {}
 
     /**
      * Content’s scrambling status is unknown
@@ -204,6 +204,23 @@
     public static final int SCRAMBLING_STATUS_SCRAMBLED =
             android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED;
 
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "MONITOR_EVENT_",
+            value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MonitorEventTypeMask {}
+
+    /**
+     * Monitor scrambling status change.
+     */
+    public static final int MONITOR_EVENT_SCRAMBLING_STATUS =
+            android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS;
+    /**
+     * Monitor ip cid change.
+     */
+    public static final int MONITOR_EVENT_IP_CID_CHANGE =
+            android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE;
 
     private static final String TAG = "Filter";
 
@@ -222,7 +239,7 @@
             int type, int subType, FilterConfiguration settings);
     private native int nativeGetId();
     private native long nativeGetId64Bit();
-    private native int nativeconfigureScramblingEvent(int scramblingStatusMask);
+    private native int nativeConfigureMonitorEvent(int monitorEventTypesMask);
     private native int nativeSetDataSource(Filter source);
     private native int nativeStartFilter();
     private native int nativeStopFilter();
@@ -306,34 +323,40 @@
     }
 
     /**
-     * Configure the Filter to monitor specific Scrambling Status through
-     * {@link ScramblingStatusEvent}.
+     * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit of
+     * {@link MonitorEventTypeMask} to monitor the change. Reset to stop monitoring.
      *
      * <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios:
-     *
      * <ul>
-     *   <li>When this method is called, the first detected scrambling status should be sent.
-     *   <li>When the filter transits into the monitored statuses configured in
-     *       {@code scramblingStatusMask}, event should be sent.
+     *   <li>When this method is called with {@link MONITOR_EVENT_SCRAMBLING_STATUS}, the first
+     *       detected scrambling status should be sent.
+     *   <li>When the Scrambling status transits into different status, event should be sent.
+     *     <ul/>
+     *
+     * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios:
+     * <ul>
+     *   <li>When this method is called with {@link MONITOR_EVENT_IP_CID_CHANGE}, the first detected
+     *       CID for the IP should be sent.
+     *   <li>When the CID is changed to different value for the IP filter, event should be sent.
      *     <ul/>
      *
      * <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version
      * will cause no-op. Use {@link TunerVersionChecker.getTunerVersion()} to get the version
      * information.
      *
-     * @param scramblingStatusMask Scrambling Statuses to be monitored. Set corresponding bit to
-     *                             monitor it. Reset to stop monitoring.
+     * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to
+     *                              monitor it. Reset to stop monitoring.
      * @return result status of the operation.
      */
     @Result
-    public int configureScramblingStatusEvent(@ScramblingStatusMask int scramblingStatusMask) {
+    public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) {
         synchronized (mLock) {
             TunerUtils.checkResourceState(TAG, mIsClosed);
             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
-                    TunerVersionChecker.TUNER_VERSION_1_1, "configureScramblingStatusEvent")) {
+                    TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) {
                 return Tuner.RESULT_UNAVAILABLE;
             }
-            return nativeconfigureScramblingEvent(scramblingStatusMask);
+            return nativeConfigureMonitorEvent(monitorEventTypesMask);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java
new file mode 100644
index 0000000..c0043f3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java
@@ -0,0 +1,46 @@
+/*
+ * 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.media.tv.tuner.filter;
+
+import android.annotation.SystemApi;
+
+/**
+ * Ip Cid Change event sent from {@link Filter} objects new ip cid.
+ *
+ * <p>This event is only sent in Tuner 1.1 or higher version. Use
+ * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
+ * @hide
+ */
+@SystemApi
+public final class IpCidChangeEvent extends FilterEvent {
+    private final int mCid;
+
+    private IpCidChangeEvent(int cid) {
+        mCid = cid;
+    }
+
+    /**
+     * Gets ip cid.
+     *
+     * <p>This event is only sent in Tuner 1.1 or higher version. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
+     */
+    public int getIpCid() {
+        return mCid;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index 52ce208..91992af 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -51,7 +51,7 @@
     public @interface TsIndexMask {}
 
     /**
-     * Invalid TS index.
+     * Invalid Transport Stream (TS) index.
      */
     public static final int TS_INDEX_INVALID = 0;
     /**
diff --git a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
index a78b96e..fef5396 100644
--- a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
+++ b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
@@ -30,18 +30,17 @@
 public final class ScramblingStatusEvent extends FilterEvent {
     private final int mScramblingStatus;
 
-    private ScramblingStatusEvent(@Filter.ScramblingStatusMask int scramblingStatus) {
+    private ScramblingStatusEvent(@Filter.ScramblingStatus int scramblingStatus) {
         mScramblingStatus = scramblingStatus;
     }
 
     /**
      * Gets Scrambling Status Type.
      *
-     * <p>This event field is only sent in Tuner 1.1 or higher version. Unsupported version returns
-     * default value 0. Use {@link TunerVersionChecker.getTunerVersion()} to get the version
-     * information.
+     * <p>This event field is only sent in Tuner 1.1 or higher version. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
      */
-    @Filter.ScramblingStatusMask
+    @Filter.ScramblingStatus
     public int getScramblingStatus() {
         return mScramblingStatus;
     }
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index ed56b43..798bf6e 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -19,8 +19,7 @@
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ContentProviderClient;
-import android.content.ContentUris;
-import android.content.ContentValues;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,7 +31,6 @@
 import android.media.ThumbnailUtils;
 import android.net.Uri;
 import android.os.BatteryManager;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.storage.StorageVolume;
 import android.provider.MediaStore;
@@ -103,8 +101,6 @@
     private MtpStorageManager mManager;
 
     private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
-    private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID};
-    private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA};
     private static final String NO_MEDIA = ".nomedia";
 
     static {
@@ -431,7 +427,7 @@
         }
         // Add the new file to MediaProvider
         if (succeeded) {
-            MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+            updateMediaStore(mContext, obj.getPath().toFile());
         }
     }
 
@@ -580,32 +576,8 @@
             return MtpConstants.RESPONSE_GENERAL_ERROR;
         }
 
-        // finally update MediaProvider
-        ContentValues values = new ContentValues();
-        values.put(Files.FileColumns.DATA, newPath.toString());
-        String[] whereArgs = new String[]{oldPath.toString()};
-        try {
-            // note - we are relying on a special case in MediaProvider.update() to update
-            // the paths for all children in the case where this is a directory.
-            final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
-            mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in mMediaProvider.update", e);
-        }
-
-        // check if nomedia status changed
-        if (obj.isDir()) {
-            // for directories, check if renamed from something hidden to something non-hidden
-            if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
-                MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile());
-            }
-        } else {
-            // for files, check if renamed from .nomedia to something else
-            if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
-                    && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
-                MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile());
-            }
-        }
+        updateMediaStore(mContext, oldPath.toFile());
+        updateMediaStore(mContext, newPath.toFile());
         return MtpConstants.RESPONSE_OK;
     }
 
@@ -635,48 +607,15 @@
             Log.e(TAG, "Failed to end move object");
             return;
         }
-
         obj = mManager.getObject(objId);
         if (!success || obj == null)
             return;
-        // Get parent info from MediaProvider, since the id is different from MTP's
-        ContentValues values = new ContentValues();
+
         Path path = newParentObj.getPath().resolve(name);
         Path oldPath = oldParentObj.getPath().resolve(name);
-        values.put(Files.FileColumns.DATA, path.toString());
-        if (obj.getParent().isRoot()) {
-            values.put(Files.FileColumns.PARENT, 0);
-        } else {
-            int parentId = findInMedia(newParentObj, path.getParent());
-            if (parentId != -1) {
-                values.put(Files.FileColumns.PARENT, parentId);
-            } else {
-                // The new parent isn't in MediaProvider, so delete the object instead
-                deleteFromMedia(obj, oldPath, obj.isDir());
-                return;
-            }
-        }
-        // update MediaProvider
-        Cursor c = null;
-        String[] whereArgs = new String[]{oldPath.toString()};
-        try {
-            int parentId = -1;
-            if (!oldParentObj.isRoot()) {
-                parentId = findInMedia(oldParentObj, oldPath.getParent());
-            }
-            if (oldParentObj.isRoot() || parentId != -1) {
-                // Old parent exists in MediaProvider - perform a move
-                // note - we are relying on a special case in MediaProvider.update() to update
-                // the paths for all children in the case where this is a directory.
-                final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
-                mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
-            } else {
-                // Old parent doesn't exist - add the object
-                MediaStore.scanFile(mContext.getContentResolver(), path.toFile());
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in mMediaProvider.update", e);
-        }
+
+        updateMediaStore(mContext, oldPath.toFile());
+        updateMediaStore(mContext, path.toFile());
     }
 
     @VisibleForNative
@@ -699,7 +638,19 @@
         if (!success) {
             return;
         }
-        MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+
+        updateMediaStore(mContext, obj.getPath().toFile());
+    }
+
+    private static void updateMediaStore(@NonNull Context context, @NonNull File file) {
+        final ContentResolver resolver = context.getContentResolver();
+        // For file, check whether the file name is .nomedia or not.
+        // If yes, scan the parent directory to update all files in the directory.
+        if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) {
+            MediaStore.scanFile(resolver, file.getParentFile());
+        } else {
+            MediaStore.scanFile(resolver, file);
+        }
     }
 
     @VisibleForNative
@@ -928,26 +879,6 @@
             deleteFromMedia(obj, obj.getPath(), obj.isDir());
     }
 
-    private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
-        final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
-
-        int ret = -1;
-        Cursor c = null;
-        try {
-            c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
-                    new String[]{path.toString()}, null, null);
-            if (c != null && c.moveToNext()) {
-                ret = c.getInt(0);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error finding " + path + " in MediaProvider");
-        } finally {
-            if (c != null)
-                c.close();
-        }
-        return ret;
-    }
-
     private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
         final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
         try {
@@ -963,13 +894,10 @@
             }
 
             String[] whereArgs = new String[]{path.toString()};
-            if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
-                if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
-                    MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile());
-                }
-            } else {
-                Log.i(TAG, "Mediaprovider didn't delete " + path);
+            if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) {
+                Log.i(TAG, "MediaProvider didn't delete " + path);
             }
+            updateMediaStore(mContext, path.toFile());
         } catch (Exception e) {
             Log.d(TAG, "Failed to delete " + path + " from MediaProvider");
         }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index b255a48..72dc32e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -604,11 +604,16 @@
 
         jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
 
-        jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts)
-                : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
-        jlong firstMbInSlice = (eventsExt.size() > i)
-                ? static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice)
-                : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+        jlong pts;
+        jlong firstMbInSlice;
+        if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+                    DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) {
+            pts = static_cast<jlong>(eventsExt[i].tsRecord().pts);
+            firstMbInSlice = static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice);
+        } else {
+            pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+            firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+        }
 
         jobject obj =
                 env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber,
@@ -632,16 +637,25 @@
 
         jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
         jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
-        jint mpuSequenceNumber = (eventsExt.size() > i)
-                ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber)
-                : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
-        jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts)
-                : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
-        jlong firstMbInSlice = (eventsExt.size() > i)
-                ? static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice)
-                : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
-        jlong tsIndexMask = (eventsExt.size() > i)
-                ? static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask) : 0;
+
+        jint mpuSequenceNumber;
+        jlong pts;
+        jlong firstMbInSlice;
+        jlong tsIndexMask;
+
+        if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+                    DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) {
+            mpuSequenceNumber = static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber);
+            pts = static_cast<jlong>(eventsExt[i].mmtpRecord().pts);
+            firstMbInSlice = static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice);
+            tsIndexMask = static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask);
+        } else {
+            mpuSequenceNumber =
+                    static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
+            pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+            firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+            tsIndexMask = 0;
+        }
 
         jobject obj =
                 env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
@@ -720,11 +734,21 @@
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent");
     jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
 
-    for (int i = 0; i < eventsExt.size(); i++) {
-        auto scramblingStatus = eventsExt[i].scramblingStatus();
-        jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus));
-        env->SetObjectArrayElement(arr, i, obj);
-    }
+    auto scramblingStatus = eventsExt[0].monitorEvent().scramblingStatus();
+    jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus));
+    env->SetObjectArrayElement(arr, 0, obj);
+    return arr;
+}
+
+jobjectArray FilterCallback::getIpCidChangeEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
+
+    auto cid = eventsExt[0].monitorEvent().cid();
+    jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(cid));
+    env->SetObjectArrayElement(arr, 0, obj);
     return arr;
 }
 
@@ -734,11 +758,9 @@
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent");
     jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
 
-    for (int i = 0; i < eventsExt.size(); i++) {
-        auto startId = eventsExt[i].startId();
-        jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId));
-        env->SetObjectArrayElement(arr, i, obj);
-    }
+    auto startId = eventsExt[0].startId();
+    jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId));
+    env->SetObjectArrayElement(arr, 0, obj);
     return arr;
 }
 
@@ -747,17 +769,31 @@
     ALOGD("FilterCallback::onFilterEvent_1_1");
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jobjectArray array;
 
     std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
     std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
-    jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL);
 
     if (events.empty() && !eventsExt.empty()) {
+        // Monitor event should be sent with one DemuxFilterMonitorEvent in DemuxFilterEventExt.
+        array = env->NewObjectArray(1, eventClazz, NULL);
         auto eventExt = eventsExt[0];
         switch (eventExt.getDiscriminator()) {
-            case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus: {
-                array = getScramblingStatusEvent(array, eventsExt);
+            case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: {
+                switch (eventExt.monitorEvent().getDiscriminator()) {
+                    case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: {
+                        array = getScramblingStatusEvent(array, eventsExt);
+                        break;
+                    }
+                    case DemuxFilterMonitorEvent::hidl_discriminator::cid: {
+                        array = getIpCidChangeEvent(array, eventsExt);
+                        break;
+                    }
+                    default: {
+                        break;
+                    }
+                }
                 break;
             }
             case DemuxFilterEventExt::Event::hidl_discriminator::startId: {
@@ -771,6 +807,7 @@
     }
 
     if (!events.empty()) {
+        array = env->NewObjectArray(events.size(), eventClazz, NULL);
         auto event = events[0];
         switch (event.getDiscriminator()) {
             case DemuxFilterEvent::Event::hidl_discriminator::media: {
@@ -4069,8 +4106,8 @@
                     ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
 }
 
-static jint android_media_tv_Tuner_configure_scrambling_status_event(
-        JNIEnv* env, jobject filter, int scramblingStatusMask) {
+static jint android_media_tv_Tuner_configure_monitor_event(
+        JNIEnv* env, jobject filter, int monitorEventType) {
     sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
     if (iFilterSp == NULL) {
         ALOGD("Failed to configure scrambling event: filter not found");
@@ -4082,7 +4119,7 @@
     Result res;
 
     if (iFilterSp_1_1 != NULL) {
-        res = iFilterSp_1_1->configureScramblingEvent(scramblingStatusMask);
+        res = iFilterSp_1_1->configureMonitorEvent(monitorEventType);
     } else {
         ALOGW("configureScramblingEvent is not supported with the current HAL implementation.");
         return (jint) Result::INVALID_STATE;
@@ -4762,8 +4799,8 @@
     { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
     { "nativeGetId64Bit", "()J",
             (void *)android_media_tv_Tuner_get_filter_64bit_id },
-    { "nativeconfigureScramblingEvent", "(I)I",
-            (void *)android_media_tv_Tuner_configure_scrambling_status_event },
+    { "nativeConfigureMonitorEvent", "(I)I",
+            (void *)android_media_tv_Tuner_configure_monitor_event },
     { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
             (void *)android_media_tv_Tuner_set_filter_data_source },
     { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index ebb95f4..e79b5c2 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -44,7 +44,6 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::kSynchronizedReadWrite;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
-using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxPid;
@@ -73,6 +72,8 @@
 using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
 using ::android::hardware::tv::tuner::V1_0::RecordStatus;
 using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
 using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
 using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
 using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
@@ -188,6 +189,8 @@
             jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
     jobjectArray getScramblingStatusEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
+    jobjectArray getIpCidChangeEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
     jobjectArray getRestartEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
 };
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index d134b37..eca67bd 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -151,6 +151,7 @@
     AHardwareBuffer_allocate; # introduced=26
     AHardwareBuffer_describe; # introduced=26
     AHardwareBuffer_fromHardwareBuffer; # introduced=26
+    AHardwareBuffer_getId; # introduced=31
     AHardwareBuffer_getNativeHandle; # introduced=26
     AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock; # introduced=26
diff --git a/packages/CompanionDeviceManager/res/layout/buttons.xml b/packages/CompanionDeviceManager/res/layout/buttons.xml
index b190a7f..a80720c 100644
--- a/packages/CompanionDeviceManager/res/layout/buttons.xml
+++ b/packages/CompanionDeviceManager/res/layout/buttons.xml
@@ -29,14 +29,15 @@
         android:id="@+id/button_cancel"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@android:string/cancel"
+        android:text="@string/consent_no"
+        android:textColor="?android:attr/textColorSecondary"
         style="@android:style/Widget.Material.Button.Borderless.Colored"
     />
     <Button
         android:id="@+id/button_pair"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@android:string/ok"
+        android:text="@string/consent_yes"
         style="@android:style/Widget.Material.Button.Borderless.Colored"
     />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/device_chooser.xml b/packages/CompanionDeviceManager/res/layout/device_chooser.xml
index db014ae..273347a 100644
--- a/packages/CompanionDeviceManager/res/layout/device_chooser.xml
+++ b/packages/CompanionDeviceManager/res/layout/device_chooser.xml
@@ -23,11 +23,13 @@
 
     <include layout="@layout/title" />
 
+    <include layout="@layout/profile_summary" />
+
     <ListView
         android:id="@+id/device_list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_below="@+id/title"
+        android:layout_below="@+id/profile_summary"
         android:layout_above="@+id/buttons"
         style="@android:style/Widget.Material.ListView"
     />
diff --git a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml b/packages/CompanionDeviceManager/res/layout/device_confirmation.xml
index 7cde41a..1336e79 100644
--- a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/device_confirmation.xml
@@ -23,6 +23,8 @@
 
     <include layout="@layout/title" />
 
+    <include layout="@layout/profile_summary" />
+
     <include layout="@layout/buttons" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/profile_summary.xml b/packages/CompanionDeviceManager/res/layout/profile_summary.xml
new file mode 100644
index 0000000..80fec59
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/profile_summary.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.
+  -->
+
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/profile_summary"
+    android:layout_below="@+id/title"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="16dp"
+    android:layout_marginEnd="16dp"
+    android:textColor="?android:attr/textColorSecondary"
+    android:textSize="14sp"
+    android:gravity="center"
+/>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/title.xml b/packages/CompanionDeviceManager/res/layout/title.xml
index 0a44fbb..9a50366 100644
--- a/packages/CompanionDeviceManager/res/layout/title.xml
+++ b/packages/CompanionDeviceManager/res/layout/title.xml
@@ -20,5 +20,6 @@
     android:id="@+id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:gravity="center"
     style="@*android:style/TextAppearance.Widget.Toolbar.Title"
 />
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index c4372eb..1b96b00 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -20,9 +20,21 @@
     <string name="app_label">Companion Device Manager</string>
 
     <!-- Title of the device selection dialog. -->
-    <string name="chooser_title">Link with &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt;</string>
+    <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%2$s</xliff:g>&lt;/strong&gt;</string>
 
-    <!-- Title of the device pairing confirmation dialog. -->
-    <string name="confirmation_title">Link &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; with &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+    <!-- The generic placeholder for a device type when nothing specific is known about it [CHAR LIMIT=30] -->
+    <string name="profile_name_generic">device</string>
+
+    <!-- Title of the device association confirmation dialog. -->
+    <string name="confirmation_title">Set &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%3$s</xliff:g>&lt;/strong&gt;</string>
+
+    <!-- Text of the device profile permissions explanation in the association dialog. -->
+    <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="app_name2" example="Android Wear">%3$s</xliff:g> will get access to <xliff:g id="permissions" example="Notifications, Calendar and Phone">%4$s</xliff:g> while the <xliff:g id="profile_name2" example="watch">%5$s</xliff:g> is connected.</string>
+
+    <!-- Positive button for the device-app association consent dialog [CHAR LIMIT=30] -->
+    <string name="consent_yes">Yes</string>
+
+    <!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] -->
+    <string name="consent_no">No thanks</string>
 
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index bdfbf82..f42a51d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,11 +17,13 @@
 package com.android.companiondevicemanager;
 
 import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.text.TextUtils.withoutPrefix;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import static java.util.Objects.requireNonNull;
 
 import android.app.Activity;
+import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -62,12 +64,19 @@
 
         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
 
-        if (getService().mRequest.isSingleDevice()) {
+        String deviceProfile = getRequest().getDeviceProfile();
+        String profileName = deviceProfile == null
+                ? getString(R.string.profile_name_generic)
+                //TODO introduce PermissionController APIs to resolve UI values
+                : withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile).toLowerCase();
+
+        if (getRequest().isSingleDevice()) {
             setContentView(R.layout.device_confirmation);
             final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
             setTitle(Html.fromHtml(getString(
                     R.string.confirmation_title,
                     getCallingAppName(),
+                    profileName,
                     selectedDevice.getDisplayName()), 0));
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
@@ -77,7 +86,9 @@
             setContentView(R.layout.device_chooser);
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setVisibility(View.GONE);
-            setTitle(Html.fromHtml(getString(R.string.chooser_title, getCallingAppName()), 0));
+            setTitle(Html.fromHtml(getString(R.string.chooser_title,
+                    profileName,
+                    getCallingAppName()), 0));
             mDeviceListView = findViewById(R.id.device_list);
             final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
             mDeviceListView.setAdapter(adapter);
@@ -97,12 +108,33 @@
             });
             mDeviceListView.addFooterView(mLoadingIndicator = getProgressBar(), null, false);
         }
+
+        TextView profileSummary = findViewById(R.id.profile_summary);
+
+        if (deviceProfile != null) {
+            //TODO introduce PermissionController APIs to resolve UI values
+            String privileges = "Notifications, Phone, Contacts and Calendar";
+            profileSummary.setVisibility(View.VISIBLE);
+            profileSummary.setText(getString(R.string.profile_summary,
+                    getCallingAppName(),
+                    profileName,
+                    getCallingAppName(),
+                    privileges,
+                    profileName));
+        } else {
+            profileSummary.setVisibility(View.GONE);
+        }
+
         getService().mActivity = this;
 
         mCancelButton = findViewById(R.id.button_cancel);
         mCancelButton.setOnClickListener(v -> cancel());
     }
 
+    private AssociationRequest getRequest() {
+        return getService().mRequest;
+    }
+
     private void cancel() {
         getService().onCancel();
         setResult(RESULT_CANCELED);
@@ -132,7 +164,7 @@
 
     @Override
     public String getCallingPackage() {
-        return requireNonNull(getService().mRequest.getCallingPackage());
+        return requireNonNull(getRequest().getCallingPackage());
     }
 
     @Override
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index a7e397e..2fe351e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -64,7 +64,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
 import android.widget.TextView;
 
 import com.android.internal.infra.AndroidFuture;
@@ -329,7 +329,7 @@
         mServiceCallback.cancel(true);
     }
 
-    class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> {
+    class DevicesAdapter extends BaseAdapter {
         private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
         private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
 
@@ -341,10 +341,6 @@
             return icon;
         }
 
-        public DevicesAdapter() {
-            super(DeviceDiscoveryService.this, 0, mDevicesFound);
-        }
-
         @Override
         public View getView(
                 int position,
@@ -391,6 +387,21 @@
             mColors.put(colorAttr, result);
             return result;
         }
+
+        @Override
+        public int getCount() {
+            return mDevicesFound.size();
+        }
+
+        @Override
+        public DeviceFilterPair getItem(int position) {
+            return mDevicesFound.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
     }
 
     /**
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index fcaf321..1697097 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -22,9 +22,9 @@
     <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар хэл, Авиа зүй"</string>
     <string name="keyboard_layout_italian" msgid="6497079660449781213">"Итали"</string>
     <string name="keyboard_layout_danish" msgid="8036432066627127851">"Дани"</string>
-    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвеги"</string>
+    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
     <string name="keyboard_layout_swedish" msgid="732959109088479351">"Швед"</string>
-    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Финлянд"</string>
+    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Финланд"</string>
     <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорват"</string>
     <string name="keyboard_layout_czech" msgid="1349256901452975343">"Чех"</string>
     <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чех хэлний QWERTY загвар"</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 495a05b..0b73271 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -37,7 +37,7 @@
     <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"तपाईंको टिभी मा <xliff:g id="APP_NAME">%1$s</xliff:g> स्थापना गर्न सकिएन।"</string>
     <string name="install_failed_msg" product="default" msgid="6484461562647915707">"तपाईंको फोनमा <xliff:g id="APP_NAME">%1$s</xliff:g> स्थापना गर्न सकिएन।"</string>
     <string name="launch" msgid="3952550563999890101">"खोल्नुहोस्"</string>
-    <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त अनुप्रयोगहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त एपहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"</string>
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"यी प्रयोगकर्ता अज्ञात एपहरू स्थापना गर्न सक्नुहुन्न"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"यो प्रयोगकर्तालाई एपहरू स्थापना गर्ने अनुमति छैन"</string>
     <string name="ok" msgid="7871959885003339302">"ठिक छ"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 5ce30c2..f67cb08 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -57,7 +57,7 @@
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcesz odinstalować tę aplikację dla "<b>"wszystkich"</b>" użytkowników? Ta aplikacja i jej dane zostaną usunięte dla "<b>"wszystkich"</b>" użytkowników na urządzeniu."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcesz odinstalować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu do pracy."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu służbowego."</string>
     <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachowaj <xliff:g id="SIZE">%1$s</xliff:g> danych aplikacji."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktywne odinstalowania"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nieudane odinstalowania"</string>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 3af4b25..64dc2af 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -51,6 +51,7 @@
         "SettingsLibRadioButtonPreference",
         "SettingsLibDisplayDensityUtils",
         "SettingsLibUtils",
+        "SettingsLibEmergencyNumber",
         "SettingsLibTopIntroPreference",
     ],
 }
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
new file mode 100644
index 0000000..3c41f78
--- /dev/null
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -0,0 +1,10 @@
+android_library {
+    name: "SettingsLibEmergencyNumber",
+
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.annotation_annotation",
+    ],
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml
new file mode 100644
index 0000000..b877fc4
--- /dev/null
+++ b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.emergencynumber">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
new file mode 100644
index 0000000..12d21ca
--- /dev/null
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -0,0 +1,133 @@
+/*
+ * 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.settingslib.emergencynumber;
+
+import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE;
+import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Util class to help manage emergency numbers
+ */
+public class EmergencyNumberUtils {
+    private static final String TAG = "EmergencyNumberUtils";
+    private static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number";
+    @VisibleForTesting
+    static final String FALL_BACK_NUMBER = "112";
+
+    private final Context mContext;
+    private final TelephonyManager mTelephonyManager;
+
+
+    public EmergencyNumberUtils(Context context) {
+        mContext = context;
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        } else {
+            mTelephonyManager = null;
+        }
+    }
+
+    /**
+     * Returns the most appropriate number for police.
+     */
+    public String getDefaultPoliceNumber() {
+        if (mTelephonyManager == null) {
+            return FALL_BACK_NUMBER;
+        }
+        final List<EmergencyNumber> promotedPoliceNumber = getPromotedEmergencyNumbers(
+                EMERGENCY_SERVICE_CATEGORY_POLICE);
+        if (promotedPoliceNumber == null || promotedPoliceNumber.isEmpty()) {
+            return FALL_BACK_NUMBER;
+        }
+        return promotedPoliceNumber.get(0).getNumber();
+    }
+
+    /**
+     * Returns the number chosen by user. If user has not provided any number, use default ({@link
+     * #getDefaultPoliceNumber()}).
+     */
+    public String getPoliceNumber() {
+        final String userProvidedNumber = Settings.Secure.getString(mContext.getContentResolver(),
+                EMERGENCY_GESTURE_CALL_NUMBER);
+        return TextUtils.isEmpty(userProvidedNumber)
+                ? getDefaultPoliceNumber() : userProvidedNumber;
+    }
+
+    private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
+        Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
+                categories);
+        if (allLists == null || allLists.isEmpty()) {
+            Log.w(TAG, "Unable to retrieve emergency number lists!");
+            return new ArrayList<>();
+        }
+        Map<Integer, List<EmergencyNumber>> promotedEmergencyNumberLists = new ArrayMap<>();
+        for (Map.Entry<Integer, List<EmergencyNumber>> entry : allLists.entrySet()) {
+            if (entry.getKey() == null || entry.getValue() == null) {
+                continue;
+            }
+            List<EmergencyNumber> emergencyNumberList = entry.getValue();
+            Log.d(TAG, "Emergency numbers for subscription id " + entry.getKey());
+
+            // The list of promoted emergency numbers which will be visible on shortcut view.
+            List<EmergencyNumber> promotedList = new ArrayList<>();
+            // A temporary list for non-prioritized emergency numbers.
+            List<EmergencyNumber> tempList = new ArrayList<>();
+
+            for (EmergencyNumber emergencyNumber : emergencyNumberList) {
+                // Emergency numbers in DATABASE are prioritized since they were well-categorized.
+                boolean isFromPrioritizedSource =
+                        emergencyNumber.getEmergencyNumberSources().contains(
+                                EMERGENCY_NUMBER_SOURCE_DATABASE);
+
+                Log.d(TAG, String.format("Number %s, isFromPrioritizedSource %b",
+                        emergencyNumber, isFromPrioritizedSource));
+                if (isFromPrioritizedSource) {
+                    promotedList.add(emergencyNumber);
+                } else {
+                    tempList.add(emergencyNumber);
+                }
+            }
+            // Puts numbers in temp list after prioritized numbers.
+            promotedList.addAll(tempList);
+
+            if (!promotedList.isEmpty()) {
+                promotedEmergencyNumberLists.put(entry.getKey(), promotedList);
+            }
+        }
+
+        if (promotedEmergencyNumberLists.isEmpty()) {
+            Log.w(TAG, "No promoted emergency number found!");
+        }
+        return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
+    }
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index fa0fa5c..2b30e0a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Onaktief. Tik om te wissel."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktief. Tik om te wissel."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Programbystandstatus:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Mediakodewisselinginstellings"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Aktiveer kodewisseling vir alle programme"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiveer kodewisseling vir programme"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Mediakodewisselinginstellings"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiveer kodewisseling"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktiveer kodewisseling vir programme"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Lopende dienste"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Sien en beheer dienste wat tans aktief is"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index f98b801..0bebfab 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ቦዝኗል። ለመቀያየር ነካ ያድርጉ።"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ገቢር። ለመቀያየር ነካ ያድርጉ።"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"የመተግበሪያ ዝግጁ የመሆን ሁኔታ፦<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"የሚዲያ ትራንስኮድ ቅንብሮች"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ለሁሉም መተግበሪያዎች ትራንስኮዲንግን ያንቁ"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ለመተግበሪያዎች ትራንስኮዲንግን ያሰናክሉ"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"የሚዲያ ትራንስኮዲንግ ቅንብሮች"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ትራንስኮንግን አሰናክል"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ለመተግበሪያዎች ትራንስኮዲንግን ያንቁ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"አሂድ አገልግሎቶች"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"በአሁኑጊዜ እየሄዱ ያሉ አገልግሎቶችን ተቆጣጠር እና እይ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"የWebView ትግበራ"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index a1432bf..6eaf3a9 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غير نشط، انقر للتبديل."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"نشط، انقر للتبديل."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"حالة تطبيق وضع الاستعداد:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"إعدادات تحويل ترميز الوسائط"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"تفعيل تحويل الترميز لكل التطبيقات"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"إيقاف تحويل الترميز للتطبيقات"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"إعدادات تحويل ترميز الوسائط"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"إيقاف تحويل الترميز"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"تفعيل تحويل الترميز للتطبيقات"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"الخدمات قيد التشغيل"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"عرض الخدمات قيد التشغيل في الوقت الحالي والتحكم فيها"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"‏تطبيق WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index c7000dd..e207e1c 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"নিষ্ক্ৰিয়। ট\'গল কৰিবলৈ টিপক।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"সক্ৰিয়। ট\'গল কৰিবলৈ টিপক।"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"এপ্ ষ্টেণ্ডবাই অৱস্থাত আছে:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"মিডিয়া ট্ৰান্সক\'ডৰ ছেটিং"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"আটাইবোৰ এপৰ বাবে ট্ৰান্সক\'ডিং সক্ষম কৰক"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"এপৰ বাবে ট্ৰান্সক\'ডিং অক্ষম কৰক"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"মিডিয়া ট্ৰান্সক\'ডিঙৰ ছেটিং"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ট্ৰান্সক\'ডিং অক্ষম কৰক"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"এপৰ বাবে ট্ৰান্সক\'ডিং সক্ষম কৰক"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"চলিত সেৱা"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"বৰ্তমান চলি থকা সেৱাসমূহ চাওক আৰু নিয়ন্ত্ৰণ কৰক"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ৱেবভিউ প্ৰয়োগ"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index c30b7b5..ff1209c 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Deaktivdir. Keçid etmək üçün basın."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivdir. Keçid etmək üçün basın."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Tətbiqin gözləmə rejimi:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media yenidən kodlaşdırma ayarları"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Bütün tətbiqlər üçün yenidən kodlaşdırmanı aktiv edin"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Tətbiqlər üçün yenidən kodlaşdırmanı deaktiv edin"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media yenidən kodlaşdırma ayarları"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Yenidən kodlaşdırmanı deaktiv edin"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Tətbiqlər üçün yenidən kodlaşdırmanı aktiv edin"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"İşləyən xidmətlər"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Hazırda prosesdə olan xidmətləri görüntüləyin və onlara nəzarət edin"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView icrası"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 5230a80..eb099c1 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivna. Dodirnite da biste je aktivirali."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivna. Dodirnite da biste je deaktivirali."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje pripravnosti aplikacije: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Podešavanja transkodiranja medija"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Omogući transkodiranje za sve aplikacije"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućite transkodiranje za aplikacije"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Podešavanja transkodiranja medija"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogući transkodiranje"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućite transkodiranje za aplikacije"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Primena WebView-a"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 04f6ef0..e6e49ab 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактыўная. Краніце, каб пераключыць."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Актыўная. Краніце, каб пераключыць."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Стан праграмы ў рэжыме чакання: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Налады перакадзіравання мультымедыя"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Дазволіць перакадзіраванне для ўсіх праграм"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Адключыць перакадзіраванне для праграм"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Налады перакадзіравання мультымедыя"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Выключыць перакадзіраванне"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Уключыць перакадзіраванне для праграм"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Запушчаныя службы"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Прагляд запушчаных службаў i кіраванне iмi"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Рэалізацыя WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index e1f18a1..251c4dd 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивно. Докоснете, за да превключите."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активно. Докоснете, за да превключите."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Състояние на готовност на приложението: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Настройки за прекодирането на мултимедийно съдържание"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Активиране на прекодирането за всички приложения"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Деактивиране на прекодирането за приложения"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Настройки за прекодирането на мултимедия"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Деактивиране на прекодирането"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Активиране на прекодирането за приложения"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Изпълнявани услуги"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Преглед и контрол върху изпълняващите се понастоящем услуги"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Внедряване на WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index b27d29c..6a99a84 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"নিষ্ক্রিয় রয়েছে৷ টগল করতে আলতো চাপুন৷"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"সক্রিয় রয়েছে৷ টগল করতে আলতো চাপুন৷"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"অ্যাপ স্ট্যান্ডবাই-এর অবস্থা:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"মিডিয়া ট্রান্সকোড সেটিংস"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"সব অ্যাপের জন্য ট্রান্সকোডিং চালু করুন"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"অ্যাপের ট্রান্সকোডিং বন্ধ করুন"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"মিডিয়া ট্রান্সকোডিং সেটিংস"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ট্রান্সকোডিং বন্ধ করুন"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"অ্যাপের জন্য ট্রান্সকোডিং চালু করুন"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"এখন চলছে যে পরিষেবাগুলি"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"বর্তমান চলমান পরিষেবাগুলি দেখুন এবং নিয়ন্ত্রণ করুন"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ওয়েবভিউ প্রয়োগ"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 8d46758..aeb7f2e 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivno. Dodirnite za promjenu opcije."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dodirnite za promjenu opcije."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje mirovanja aplikacije:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Postavke transkodiranja medijskih fajlova"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Omogućite transkodiranje za sve aplikacije"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućite transkodiranje za aplikacije"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Postavke transkodiranja medija"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogućite transkodiranje"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućite transkodiranje za aplikacije"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Postavljanje WebViewa"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f4fc7d6..df4c99e 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Aplicació inactiva. Toca per activar-la."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aplicació activa. Toca per desactivar-la."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estat de les aplicacions inactives: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configuració de la transcodificació de contingut multimèdia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Activa la transcodificació per a totes les aplicacions"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desactiva la transcodificació per a les aplicacions"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configuració de la transcodificació de contingut multimèdia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desactiva la transcodificació"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Activa la transcodificació per a les aplicacions"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Serveis en execució"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualitza i controla els serveis en execució"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementació de WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index be0bd47..e563ca5 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivní. Klepnutím možnost přepnete."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivní. Klepnutím možnost přepnete."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stav pohotovostního režimu aplikace: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavení překódování médií"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Povolit překódování u všech aplikací"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Zakázat překódování aplikací"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavení překódování médií"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Zakázat překódování"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Povolit překódování pro aplikace"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Spuštěné služby"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Umožňuje zobrazit a ovládat aktuálně spuštěné služby"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementace WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 2725598..8a3d054 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Tryk for at skifte."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Tryk for at skifte."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Standbystatus for appen:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Indstillinger for omkodning af medier"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivér omkodning for alle apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiver omkodning for apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Indstillinger for omkodning af medier"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiver omkodning"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivér omkodning for apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Kørende tjenester"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vis og administrer kørende tjenester"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index fcd1222..624b299 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -399,11 +399,11 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Zum Wechseln tippen."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Zum Wechseln tippen."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Standby-Status der App:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (6700974145733932357) -->
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
     <skip />
-    <!-- no translation found for transcode_enable_all (4719796495995795404) -->
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
     <skip />
-    <!-- no translation found for transcode_skip_apps (5680997722349545778) -->
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
     <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string>
@@ -452,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 9e8845a..7ea9050 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ανενεργό. Πατήστε για εναλλαγή."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ενεργό. Πατήστε για εναλλαγή."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Κατάσταση αναμονής εφαρμογής:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Ρυθμίσεις διακωδικοποίησης μέσων"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Ενεργοποίηση διακωδικοποίησης για όλες τις εφαρμογές"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Απενεργοποίηση της διακωδικοποίησης για εφαρμογές"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Ρυθμίσεις διακωδικοποίησης μέσων"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Απενεργοποίηση διακωδικοποίησης"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ενεργοποίηση διακωδικοποίησης για εφαρμογές"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Υπηρεσίες που εκτελούνται"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Προβολή και έλεγχος των εφαρμογών που εκτελούνται αυτή τη στιγμή"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Υλοποίηση WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 7729f8e..f8019d2 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 00be355..7135de5 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 7729f8e..f8019d2 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 7729f8e..f8019d2 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index dacc782..54290d2 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎Inactive. Tap to toggle.‎‏‎‎‏‎"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎Active. Tap to toggle.‎‏‎‎‏‎"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‎App standby state:‎‏‎‎‏‏‎<xliff:g id="BUCKET"> %s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎Media transcode settings‎‏‎‎‏‎"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎Enable transcoding for all apps‎‏‎‎‏‎"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎Disable transcoding for apps‎‏‎‎‏‎"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‎Media transcoding settings‎‏‎‎‏‎"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎Disable transcoding‎‏‎‎‏‎"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎Enable transcoding for apps‎‏‎‎‏‎"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎Running services‎‏‎‎‏‎"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎View and control currently running services‎‏‎‎‏‎"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎WebView implementation‎‏‎‎‏‎"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until charged‎‏‎‎‏‎"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ until charged‎‏‎‎‏‎"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Battery limited temporarily‎‏‎‎‏‎"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎Unknown‎‏‎‎‏‎"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎Charging‎‏‎‎‏‎"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎Charging rapidly‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 40eb0c7..7affb5c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactiva. Presiona para activar o desactivar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activa. Presiona para activar o desactivar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado de la app en espera: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de la transcodificación multimedia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Habilitar la transcodificación para todas las apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Inhabilitar la transcodificación para las apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de transcodificación de contenido multimedia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Inhabilitar transcodificación"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Habilitar transcodificación en apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"En ejecución"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar servicios actuales en ejecución"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 8366917..ccdc1c4 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactiva. Toca para alternar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activa. Toca para alternar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado de la aplicación en espera: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de la transcodificación multimedia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Habilitar transcodificación en todas las aplicaciones"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Inhabilitar transcodificación en las aplicaciones"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de la transcodificación multimedia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Inhabilitar transcodificación"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Habilitar transcodificación en las aplicaciones"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicios en ejecución"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar los servicios en ejecución"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> hasta cargarse completamente"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta cargarse completamente)"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f876b95..852d6df 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Passiivne. Puudutage vahetamiseks."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivne. Puudutage vahetamiseks."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Rakenduse ootelolek:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Meedia transkodeerimise seaded"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Luba transkodeerimine kõikide rakenduste puhul"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Keela rakenduste puhul transkodeerimine"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Meedia transkodeerimise seaded"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Keela transkodeerimine"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Luba rakenduste puhul transkodeerimine"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Käitatud teenused"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Praegu käitatud teenuste vaatamine ja juhtimine"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView\' rakendamine"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 0a7bc3e..6b6b9b6 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktibo. Aldatzeko, sakatu hau."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. Aldatzeko, sakatu hau."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Egonean moduko aplikazioaren egoera: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Multimedia-edukia transkodetzeko ezarpenak"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Gaitu transkodetzeko aukera aplikazio guztietan"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desgaitu aplikazioak transkodetzeko aukera"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Multimedia-edukia transkodetzeko ezarpenak"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desgaitu transkodetzeko aukera"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Gaitu aplikazioak transkodetzeko aukera"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Abian diren zerbitzuak"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu une honetan abian diren zerbitzuak"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView inplementazioa"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index e0f3280..af1e55b 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیرفعال. برای تغییر حالت ضربه بزنید."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال. برای تغییر حالت ضربه بزنید."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"وضعیت حالت آماده به‌کار برنامه:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"تنظیمات تراتبدیل رسانه"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"فعال کردن تراتبدیل برای همه برنامه‌ها"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"غیرفعال کردن تراتبدیل برای برنامه‌ها"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"تنظیمات تراتبدیل رسانه"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"غیرفعال کردن تراتبدیل"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"فعال کردن تراتبدیل برای برنامه‌ها"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"سرویس‌های در حال اجرا"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"مشاهده و کنترل سرویس‌های در حال اجرای فعلی"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"اجرای وب‌نما"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 651faa5..8e819ef 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ei päällä. Ota käyttöön koskettamalla."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivinen. Vaihda koskettamalla."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Sovelluksen valmiusluokka: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Median transkoodausasetukset"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Ota transkoodaus käyttöön kaikissa sovelluksissa"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Poista sovellusten transkoodaus käytöstä"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Median transkoodausasetukset"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Poista transkoodaus käytöstä"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ota sovellusten transkoodaus käyttöön"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tarkastele ja hallitse käynnissä olevia palveluita."</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-käyttöönotto"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index c37d6cc..4754d15 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -142,7 +142,7 @@
     <string name="process_kernel_label" msgid="950292573930336765">"Système d\'exploitation Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Applications supprimées"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Applications et utilisateurs supprimés"</string>
-    <string name="data_usage_ota" msgid="7984667793701597001">"Mises à jour système"</string>
+    <string name="data_usage_ota" msgid="7984667793701597001">"Mises à jour du système"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"Partage de connexion par USB"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Point d\'accès Wi-Fi mobile"</string>
     <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Partage connexion Bluetooth"</string>
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Application inactive. Touchez ici pour l\'activer."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Application active. Touchez ici pour la désactiver."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"État de l\'application en veille :<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Paramètres de transcodage pour les éléments multimédias"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Activer le transcodage pour toutes les applications"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Désactiver le transcodage pour toutes les applications"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Paramètres de transcodage des éléments multimédias"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Désactiver le transcodage"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Activer le transcodage pour les applications"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
@@ -501,7 +503,7 @@
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
     <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jamais"</string>
-    <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Priorités seulement"</string>
+    <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Prioritaires seulement"</string>
     <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="zen_alarm_warning_indef" msgid="4146527909616457163">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g> sauf si vous désactivez préalablement cette option"</string>
     <string name="zen_alarm_warning" msgid="245729928048586280">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index ef52ec5..a3863f59 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Application inactive. Appuyez ici pour l\'activer."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Application active. Appuyez ici pour la désactiver."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"État de mise en veille de l\'application : <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Paramètres de transcodage des contenus multimédias"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Activer le transcodage pour toutes les applications"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Désactiver le transcodage pour les applications"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Paramètres de transcodage des contenus multimédias"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Désactiver le transcodage"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Activer le transcodage pour les applications"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 16777aa..81e79e4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Aplicación inactiva. Toca para alternar a configuración."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aplicación activa. Toca para alternar a configuración."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado en espera da aplicación: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de transcodificación de contido multimedia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Activar transcodificación para todas as aplicacións"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desactivar transcodificación para determinadas aplicacións"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de transcodificación de contido multimedia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desactivar transcodificación"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Activar transcodificación para as aplicacións"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizos en uso"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Comproba e controla os servizos actualmente en uso"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index ef88ab1..9bf0970 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -399,9 +399,12 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"નિષ્ક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"સક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ઍપ સ્ટૅન્ડબાયની સ્થિતિ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"બધી ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string>
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
+    <skip />
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
+    <skip />
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
+    <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string>
@@ -449,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 3aa413b..151b861 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"बंद है. टॉगल करने के लिए टैप करें."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करने के लिए टैप करें."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ऐप्लिकेशन स्टैंडबाय की स्थिति:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"मीडिया ऐप्लिकेशन को ट्रांसकोड करने से जुड़ी सेटिंग"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"सभी ऐप्लिकेशन के लिए ट्रांसकोडिंग चालू करें"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ऐप्लिकेशन के लिए ट्रांसकोडिंग बंद करें"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रांसकोडिंग सेटिंग"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रांसकोडिंग को बंद करें"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ऐप्लिकेशन के लिए ट्रांसकोडिंग चालू करें"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"चल रही सेवाएं"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"इस समय चल रही सेवाओं को देखें और नियंत्रित करें"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबव्यू लागू करें"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index bc0c2bc..d37879f 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nije aktivno. Dodirnite da biste to promijenili."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dodirnite da biste to promijenili."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje aplikacije u mirovanju: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Postavke konvertiranja medija"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Omogućavanje konvertiranja za sve aplikacije"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućivanje konvertiranja za aplikacije"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Postavke konvertiranja medija"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogućivanje konvertiranja"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućivanje konvertiranja za aplikacije"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pregledajte i kontrolirajte pokrenute usluge"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacija WebViewa"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 37b5b50..4ac5308 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Kikapcsolva. Koppintson ide a váltáshoz."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Bekapcsolva. Koppintson ide a váltáshoz."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Alkalmazás készenléti állapota:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Média átkódolásához tartozó beállítások"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Átkódolás engedélyezése minden alkalmazásnál"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Átkódolás letiltása az alkalmazásoknál"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Médiaátkódolási beállítások"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Átkódolás letiltása"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Átkódolás engedélyezése az alkalmazásoknál"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Futó szolgáltatások"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"A jelenleg futó szolgáltatások megtekintése és vezérlése"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-megvalósítás"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésből"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index f2744b2..b0734fd 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ակտիվ չէ: Հպեք՝ փոխելու համար:"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ակտիվ է: Հպեք՝ փոխելու համար:"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Հավելվածի սպասման կարգավիճակ՝ <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Մեդիա բովանդակության վերակոդավորման կարգավորումներ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Միացնել վերակոդավորումը բոլոր հավելվածների համար"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Անջատել վերակոդավորումը հավելվածների համար"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Մեդիա ֆայլերի վերակոդավորման կարգավորումներ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Անջատել վերակոդավորումը"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Միացնել վերակոդավորումը հավելվածների համար"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Աշխատող ծառայություններ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Դիտել և վերահսկել ընթացիկ աշխատող ծառայությունները"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ծառայություն"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index e129827..9db21c7 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Tidak aktif. Ketuk untuk beralih."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktif. Ketuk untuk beralih."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Status standby aplikasi:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Setelan transcoding media"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Mengaktifkan transcoding untuk semua aplikasi"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Menonaktifkan transcoding untuk aplikasi"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Setelan transcoding media"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Menonaktifkan transcoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Mengaktifkan transcoding untuk aplikasi"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Layanan yang sedang berjalan"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Melihat dan mengontrol layanan yang sedang berjalan"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Penerapan WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 724cf63..d9ee03a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Óvirkt. Ýttu til að breyta."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Virkt. Ýttu til að breyta."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Biðstaða forrits: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Umkóðunarstillingar efnis"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Kveikja á umkóðun í öllum forritum"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Slökkva á umkóðun í forritum"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Stillingar efnisumkóðunar"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Slökkva á umkóðun"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Kveikja á umkóðun í forritum"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Þjónustur í gangi"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Skoða og stjórna þjónustum í gangi"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Innleiðing WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 4edb887..57378e7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Non attiva. Tocca per attivare/disattivare."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Attiva. Tocca per attivare/disattivare."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stato di standby dell\'app: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Impostazioni transcodifica contenuti multimediali"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Attiva transcodifica per tutte le app"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Disattiva transcodifica per le app"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Impostazioni transcodifica contenuti multimediali"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Disattiva transcodifica"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Attiva transcodifica per le app"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizi in esecuzione"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizza e controlla i servizi attualmente in esecuzione"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementazione di WebView"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo rimanente alla carica completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batteria momentaneamente limitata"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 6c916d9..fd53f14 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -399,9 +399,12 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"אפליקציה לא פעילה. הקש כדי להחליף מצב."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"אפליקציה פעילה. הקש כדי להחליף מצב."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"אפליקציה במצב המתנה:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"הגדרות המרת קידוד במדיה"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"הפעלת המרת קידוד בכל האפליקציות"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"השבתת המרת קידוד באפליקציות"</string>
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
+    <skip />
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
+    <skip />
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
+    <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"‏יישום WebView"</string>
@@ -449,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 69392ce..c0aa74b 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"無効です。タップすると切り替わります。"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"有効です。タップすると切り替わります。"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"アプリ スタンバイ状態: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"メディアのコード変換設定"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"すべてのアプリに対しコード変換を有効にする"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"アプリに対しコード変換を無効にする"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"メディアのコード変換設定"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"コード変換を無効にする"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"アプリに対しコード変換を有効にする"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"実行中のサービス"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"現在実行中のサービスを表示して制御する"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView の実装"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index e815621..d188578 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"უმოქმედო. შეეხეთ გადასართავად."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"აქტიური. შეეხეთ გადასართავად."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"აპის მოლოდინის რეჟიმის მდგომარეობა:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"მედიის ტრანსკოდირების პარამეტრები"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ტრანსკოდირების ჩართვა ყველა აპისთვის"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ტრანსკოდირების გამორთვა ყველა აპისთვის"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"მედიის ტრანსკოდირების პარამეტრები"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ტრანსკოდირების გათიშვა"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ტრანსკოდირების ჩართვა აპებისთვის"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"მიმდინარე სერვისები"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ამჟამად მოქმედი სერვისების ნახვა და მართვა"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView რეალიზაცია"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"დატენვამდე დარჩა <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> დატენვამდე"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> — ბატარეა დროებით შეზღუდულია"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index c75c722..09f3ff5 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Белсенді емес. Ауыстырып қосу үшін түртіңіз."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Белсенді. Ауыстырып қосу үшін түртіңіз."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Қолданбаның күту режимі: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Медиамазмұнды қайта кодтау параметрлері"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Барлық қолданба үшін қайта кодтауға рұқсат ету"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Қолданбалар үшін қайта кодтауды өшіру"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Медиамазмұнды қайта кодтау параметрлері"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Қайта кодтауды өшіру"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Қолданбалар үшін қайта кодтауға рұқсат ету"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Қосылып тұрған қызметтер"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Қазір істеп тұрған қызметтерді көру және басқару"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView қызметі"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2590a9f..42e6a53 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"សកម្ម។ ប៉ះដើម្បីបិទ/បើក។"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"សកម្ម។ ប៉ះដើម្បីបិទ/បើក។"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ស្ថាន​ភាព​មុខងារ​ផ្អាក​ដំណើរការ​កម្មវិធី៖<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"ការកំណត់​ការបំប្លែង​កូដមេឌៀ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"បើក​ការបំប្លែងកូដ​សម្រាប់​កម្មវិធីទាំងអស់"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"បិទ​ការបំប្លែងកូដ​សម្រាប់​កម្មវិធី"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ការកំណត់​ការបំប្លែង​កូដមេឌៀ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"បិទ​ការបំប្លែងកូដ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"បើក​ការបំប្លែងកូដ​សម្រាប់​កម្មវិធី"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"សេវាកម្ម​កំពុង​ដំណើរការ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"មើល និង​គ្រប់គ្រង​សេវាកម្ម​កំពុង​ដំណើរការ​បច្ចុប្បន្ន"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ការអនុវត្ត WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើប​សាកថ្មពេញ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"មិន​ស្គាល់"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូល​ថ្ម"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index dad3e03..658b987 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ನಿಷ್ಕ್ರಿಯ. ಟಾಗಲ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ಸಕ್ರಿಯ. ಟಾಗಲ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಟ್ಯಾಂಡ್‌ಬೈ ಸ್ಥಿತಿ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"ಮಾಧ್ಯಮ ಟ್ರಾನ್ಸ್‌ಕೋಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ಮೀಡಿಯಾ ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ರನ್‌ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳು"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ಈಗ ರನ್‌ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಹೊಂದಿಸಿ"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d41e7db..2b0843d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"비활성화 상태입니다. 전환하려면 탭하세요."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"활성화되었습니다. 전환하려면 탭하세요."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"앱 대기 상태:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"미디어 트랜스코딩 설정"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"모든 앱에서 트랜스코딩 사용 설정"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"모든 앱에서 트랜스코딩 사용 중지"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"미디어 트랜스코딩 설정"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"트랜스코딩 사용 중지"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"앱 트랜스코딩 사용 설정"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"실행 중인 서비스"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"현재 실행 중인 서비스 보기 및 제어"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 구현"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index c07f334..499fd88 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Медианы транскоддоо жөндөөлөрү"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Бардык колдонмолор үчүн транскоддоону иштетүү"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Бардык колдонмолор үчүн транскоддоону өчүрүү"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо жөндөөлөрү"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Транскоддоону өчүрүү"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Колдонмолорду транскоддоону күйгүзүү"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView кызматы"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 5aa27bf..a5be601 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ບໍ່ໄດ້ນຳໃຊ້. ແຕະບໍ່ສັບປ່ຽນ."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ນຳໃຊ້ຢູ່. ແຕະເພື່ອສັບປ່ຽນ."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ສະຖານະສະແຕນບາຍແອັບ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"ການຕັ້ງຄ່າການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດມີເດຍ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ເປີດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດສຳລັບທຸກແອັບ"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ປິດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດສຳລັບແອັບ"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດມີເດຍ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດສຳລັບແອັບ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index ff4d336..181fb10 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktyvi. Palieskite, kad perjungtumėte."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktyvi. Palieskite, kad perjungtumėte."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Programų budėjimo režimo būsena: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Medijos perkodavimo nustatymai"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Įjungti visų programų perkodavimą"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Išjungti programų perkodavimą"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Medijos perkodavimo nustatymai"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Išjungti perkodavimą"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Įjungti programų perkodavimą"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Vykdomos paslaugos"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Žiūrėti ir valdyti dabar vykdomas paslaugas"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"„WebView“ diegimas"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index e876cef..717e2b0 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktīva. Pieskarieties, lai pārslēgtu."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktīva. Pieskarieties, lai pārslēgtu."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Lietotnes gaidstāves stāvoklis: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Multivides failu pārkodēšanas iestatījumi"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Iespējot pārkodēšanu visām lietotnēm"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Atspējot pārkodēšanu noteiktām lietotnēm"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Multivides failu pārkodēšanas iestatījumi"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Atspējot pārkodēšanu"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Iespējot pārkodēšanu noteiktām lietotnēm"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktīvie pakalpojumi"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pašreiz darbojošos pakalpojumu skatīšana un vadība"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ieviešana"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 3507ae7..2ccb150 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -51,9 +51,9 @@
     <string name="connected_via_carrier" msgid="1968057009076191514">"Поврзано преку %1$s"</string>
     <string name="available_via_carrier" msgid="465598683092718294">"Достапно преку %1$s"</string>
     <string name="osu_opening_provider" msgid="4318105381295178285">"Се отвора <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
-    <string name="osu_connect_failed" msgid="9107873364807159193">"Не можеше да се поврзе"</string>
+    <string name="osu_connect_failed" msgid="9107873364807159193">"Не може да се поврзе"</string>
     <string name="osu_completing_sign_up" msgid="8412636665040390901">"Се завршува регистрацијата…"</string>
-    <string name="osu_sign_up_failed" msgid="5605453599586001793">"Не можеше да се заврши регистрацијата. Допрете за да се обидете повторно."</string>
+    <string name="osu_sign_up_failed" msgid="5605453599586001793">"Не може да се заврши регистрацијата. Допрете за да се обидете повторно."</string>
     <string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрацијата е завршена. Се поврзува…"</string>
     <string name="speed_label_very_slow" msgid="8526005255731597666">"Многу бавна"</string>
     <string name="speed_label_slow" msgid="6069917670665664161">"Бавна"</string>
@@ -116,8 +116,8 @@
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАРИ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Откажи"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Кога е поврзано, спарувањето одобрува пристап до контактите и историјата на повиците."</string>
-    <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или лозинка."</string>
+    <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или лозинка."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Не може да комуницира со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Спарувањето е одбиено од <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Компјутер"</string>
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивно. Допрете за да смените."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активно. Допрете за да смените."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Состојба на мирување на апликацијата: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Поставки за транскодирање на аудиовизуелни содржини"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Овозможи транскодирање за сите апликации"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Оневозможи транскодирање за сите апликации"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Поставки за транскодирање аудиовизуелни содржини"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Оневозможи транскодирање"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Овозможете транскодирање за апликациите"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Активни услуги"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Погледнете и контролирајте услуги што се моментално активни"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Воведување WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 4aae6ee..768ad5e 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -399,11 +399,11 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"നിഷ്‌ക്രിയം. മാറ്റുന്നതിനു ടാപ്പുചെയ്യുക."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"സജീവം. മാറ്റുന്നതിന് ടാപ്പുചെയ്യുക."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ആപ്പ് സ്‌റ്റാൻഡ്‌ബൈ നില:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (6700974145733932357) -->
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
     <skip />
-    <!-- no translation found for transcode_enable_all (4719796495995795404) -->
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
     <skip />
-    <!-- no translation found for transcode_skip_apps (5680997722349545778) -->
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
     <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string>
@@ -452,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 9cdf5bf..61ebca5 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Медиагийн хөрвүүлгийн тохиргоо"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Бүх аппад хөрвүүлгийг идэвхжүүлэх"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Аппуудад хөрвүүлгийг идэвхгүй болгох"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Хөрвүүлгийг идэвхгүй болгох"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Аппуудад хөрвүүлгийг идэвхжүүлэх"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Ажиллаж байгаа үйлчилгээнүүд"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Одоо ажиллаж байгаа үйлчилгээнүүдийг харах болон хянах"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView хэрэгжилт"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index d6563ae..ef319c3 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -399,11 +399,11 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"अ‍ॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (6700974145733932357) -->
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
     <skip />
-    <!-- no translation found for transcode_enable_all (4719796495995795404) -->
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
     <skip />
-    <!-- no translation found for transcode_skip_apps (5680997722349545778) -->
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
     <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string>
@@ -452,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 6bcc5d2..83c0384 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Tidak aktif. Ketik untuk menogol."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktif. Ketik untuk menogol."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Keadaan tunggu sedia apl:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Tetapan transpengekodan media"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Dayakan transpengekodan untuk semua apl"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Lumpuhkan transpengekodan untuk apl"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Tetapan transpengekodan media"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Lumpuhkan transpengekodan"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Dayakan transpengekodan untuk apl"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Perkhidmatan dijalankan"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Lihat dan kawal perkhidmatan yang sedang dijalankan"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Pelaksanaan WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 8bb717a..f398487 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ပွင့်မနေပါ။ ပြောင်းရန်တို့ပါ။"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ပွင့်နေသည်။ ပြောင်းရန်တို့ပါ။"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"အက်ပ်ကို အရန်သင့်ထားရှိခြင်း အခြေအနေ-<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"မီဒီယာအမျိုးအစားပြောင်းခြင်း ဆက်တင်များ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"အက်ပ်အားလုံးအတွက် အမျိုးအစားပြောင်းခြင်းကို ဖွင့်ရန်"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"အက်ပ်များအတွက် အမျိုးအစားပြောင်းခြင်းကို ပိတ်ရန်"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"မီဒီယာအမျိုးအစားပြောင်းခြင်း ဆက်တင်များ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"အမျိုးအစားပြောင်းခြင်းကို ပိတ်ရန်"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"အက်ပ်များအတွက် အမျိုးအစားပြောင်းခြင်းကို ဖွင့်ရန်"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"အလုပ်လုပ်နေသောဝန်ဆောင်မှုများ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"လက်ရှိ ဝန်ဆောင်မှုများကို ကြည့်ရှု ထိန်းသိမ်းသည်"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView အကောင်အထည်ဖော်မှု"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 6a3aa29..0ef5809 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ikke aktiv. Trykk for å slå av/på."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Trykk for å slå av/på."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Hvilemodus:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Innstillinger for omkoding av medier"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Aktiver omkoding for alle apper"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiver omkoding for apper"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Innstillinger for omkoding av medier"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiver omkoding"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktiver omkoding for apper"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive tjenester"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Se og kontrollér tjenester som kjører"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 5bc98c1..b956e21 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -305,7 +305,7 @@
     <string name="adbwifi_warning_message" msgid="8005936574322702388">"वायरलेस डिबगिङ डिभलपमेन्ट प्रयोजनका लागि मात्रै हो। यसलाई आफ्ना कम्प्युटर र उपकरणका बिच डेटा प्रतिलिपि गर्न, सूचना नदिई आफ्नो उपकरणमा एपहरू स्थापना गर्न र लग डेटा पढ्न प्रयोग गर्नुहोस्।"</string>
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"तपाईं पहिले नै अधिकृत गर्नुभएका सबै कम्प्यूटरबाट USB डिबग गर्नको लागि पहुँच रद्द गर्ने हो?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"विकास सेटिङहरू अनुमति दिने हो?"</string>
-    <string name="dev_settings_warning_message" msgid="37741686486073668">"यी सेटिङहरू केवल विकास प्रयोगको लागि विचार गरिएको हो। तिनीहरूले तपाईंको उपकरण र अनुप्रयोगहरूलाई विच्छेदन गर्न वा दुर्व्यवहार गर्न सक्दछ।"</string>
+    <string name="dev_settings_warning_message" msgid="37741686486073668">"यी सेटिङहरू केवल विकास प्रयोगको लागि विचार गरिएको हो। तिनीहरूले तपाईंको उपकरण र एपहरूलाई विच्छेदन गर्न वा दुर्व्यवहार गर्न सक्दछ।"</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB मा एपहरू रुजु गर्नुहोस्"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहारको लागि ADB/ADT को माध्यमबाट स्थापित अनुप्रयोगहरूको जाँच गर्नुहोस्।"</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू (MAC ठेगाना भएका मात्र) देखाइनेछ"</string>
@@ -399,9 +399,12 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"मिडिया फाइल ट्रान्सकोड गर्नेसम्बन्धी सेटिङ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"सबै एपमा ट्रान्सकोडिङ अन गर्नुहोस्"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"एपमा ट्रान्सकोडिङ अफ गर्नुहोस्"</string>
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
+    <skip />
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
+    <skip />
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
+    <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string>
@@ -449,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index bf40f40..3223857 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactief. Tik om te schakelen."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Actief. Tik om te schakelen."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stand-bystatus app: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Instellingen voor mediatranscodering"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Transcodering inschakelen voor alle apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Transcodering uitschakelen voor alle apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Instellingen voor mediatranscodering"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodering uitschakelen"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodering inschakelen voor apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Actieve services"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Services die momenteel actief zijn, weergeven en beheren"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementatie"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index a65a7d8..11bb550 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -399,9 +399,12 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ନିଷ୍କ୍ରିୟ। ଟୋଗଲ୍‌ କରିବାକୁ ଟାପ୍‌ କରନ୍ତୁ।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ସକ୍ରିୟ। ବଦଳାଇବା ପାଇଁ ଟାପ୍‌ କରନ୍ତୁ"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ଆପ୍ ଷ୍ଟାଣ୍ଡବାଏ ଅବସ୍ଥା:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ସମସ୍ତ ଆପ୍ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
+    <skip />
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
+    <skip />
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
+    <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍‌ଭ୍ୟୁ ପ୍ରୟୋଗ"</string>
@@ -449,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 5a009be..8d66f3c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -399,9 +399,12 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ਅਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ਐਪ ਸਟੈਂਡਬਾਈ ਸਥਿਤੀ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"ਮੀਡੀਆ ਦੀਆਂ ਟ੍ਰਾਂਸਕੋਡ ਸੈਟਿੰਗਾਂ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"ਸਾਰੀਆਂ ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ਸਾਰੀਆਂ ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string>
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
+    <skip />
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
+    <skip />
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
+    <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string>
@@ -449,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 294e628..ed96c46 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -148,7 +148,7 @@
     <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Tethering przez Bluetooth"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Tethering"</string>
     <string name="tether_settings_title_all" msgid="8910259483383010470">"Tethering i punkt dostępu"</string>
-    <string name="managed_user_title" msgid="449081789742645723">"Wszystkie aplikacje do pracy"</string>
+    <string name="managed_user_title" msgid="449081789742645723">"Wszystkie aplikacje służbowe"</string>
     <string name="user_guest" msgid="6939192779649870792">"Gość"</string>
     <string name="unknown" msgid="3544487229740637809">"Nieznana"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Użytkownik: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nieaktywna. Dotknij, by zmienić."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktywna. Dotknij, by zmienić."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stan aplikacji w trybie czuwania: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Ustawienia transkodowania multimediów"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Włącz transkodowanie dla wszystkich aplikacji"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Wyłącz transkodowanie dla aplikacji"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Ustawienia transkodowania multimediów"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Wyłącz transkodowanie"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Włącz transkodowanie dla aplikacji"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Uruchomione usługi"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Wyświetl obecnie uruchomione usługi i nimi zarządzaj"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacja WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d9d1973..acaabc7 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Tocar para alternar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Tocar para alternar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado em espera do app:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configurações de transcodificação de mídia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar a transcodificação para todos os apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desativar a transcodificação para apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configurações de transcodificação de mídia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar transcodificação"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ativar transcodificação para apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporariamente"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index ee6e5fe..2c1a967 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Definições da transcodificação multimédia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar transcodificação para todas as apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desative a transcodificação para apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Definições da transcodificação de multimédia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar a transcodificação"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ative a transcodificação para apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços actualmente em execução"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação WebView"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> até ficar carregada"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar carregada"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Bateria limitada temporariamente."</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d9d1973..acaabc7 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Tocar para alternar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Tocar para alternar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado em espera do app:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Configurações de transcodificação de mídia"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar a transcodificação para todos os apps"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Desativar a transcodificação para apps"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Configurações de transcodificação de mídia"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar transcodificação"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ativar transcodificação para apps"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string>
@@ -449,6 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporariamente"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8f6ddeb..46d6d0a 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactivă. Atingeți pentru a comuta."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activă. Atingeți pentru a comuta."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stare Standby aplicații: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Setări pentru transcodarea conținutului media"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Activați transcodarea pentru toate aplicațiile"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Dezactivați transcodarea pentru aplicații"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Setări pentru transcodarea conținutului media"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Dezactivați transcodarea"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Activați transcodarea pentru aplicații"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicii în curs de funcționare"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vedeți și controlați serviciile care funcționează în prezent"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementare WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index e61eb29..2a79ad3 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Выключено. Нажмите, чтобы включить."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Включено. Нажмите, чтобы отключить."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Статус приложения в режиме ожидания:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Настройки перекодирования медиаконтента"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Разрешить перекодирование для всех приложений"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Приложения, для которых нужно запретить перекодирование"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Настройки перекодирования медиафайлов"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Отключить перекодирование"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Включить перекодирование для приложений"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Работающие службы"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Просмотр и управление работающими службами"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Сервис WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index cfea2aa..78f3558 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"අක්‍රියයි. ටොගල කිරීමට තට්ටු කරන්න."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"සක්‍රියයි. ටොගල කිරීමට තට්ටු කරන්න."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"යෙදුම් පොරොත්තු තත්ත්වය:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"මාධ්‍ය ට්‍රාන්ස්කෝඩ් සැකසීම්"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"සියලු යෙදුම් සඳහා ට්‍රාන්ස්කෝඩින් සබල කරන්න"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"සියලු යෙදුම් සඳහා ට්‍රාන්ස්කෝඩින් අබල කරන්න"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"මාධ්‍ය ට්‍රාන්ස්කෝඩින් සැකසීම්"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ට්‍රාන්ස්කෝඩින් අබල කරන්න"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"යෙදුම් සඳහා ට්‍රාන්ස්කෝඩින් සබල කරන්න"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ධාවනය වන සේවා"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"දැනට ධාවනය වන සේවා බලන්න සහ පාලනය කරන්න"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ක්‍රියාත්මක කිරීම"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 56fc811..152c7a8 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktívne. Prepnite klepnutím."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktívne. Prepnite klepnutím."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stav pohotovostného režimu aplikácie: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavenie prekódovania médií"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Povoliť prekódovanie všetkých aplikácií"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Zakázať prekódovanie všetkých aplikácií"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavenia prekódovania médií"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Vypnúť prekódovanie"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Zapnúť prekódovanie aplikácií"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Spustené služby"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Zobrazovať a riadiť aktuálne spustené služby"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementácia WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 22a6570..a2f093e 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivno. Dotaknite se za preklop."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dotaknite se za preklop."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje pripravljenosti aplikacije: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavitve prekodiranja predstavnosti"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Omogočanje prekodiranja za vse aplikacije"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogočanje prekodiranja za aplikacije"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavitve prekodiranja predstavnosti"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogočanje prekodiranja"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogočanje prekodiranja za aplikacije"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Zagnane storitve"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Preglejte in nadzorujte storitve, ki so trenutno zagnane"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Izvedba spletnega pogleda"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 2d846c8..b458917 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Joaktiv. Trokit për ta ndryshuar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Trokit për ta ndryshuar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Gjendja e gatishmërisë e aplikacionit:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Cilësimet e transkodimit të medias"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivizo transkodimin për të gjitha aplikacionet"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Çaktivizo transkodimin për aplikacionet"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Cilësimet e transkodimit të multimediave"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Çaktivizo transkodimin"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivizo transkodimin për aplikacionet"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Shërbimet në ekzekutim"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Shiko dhe kontrollo shërbimet që po ekzekutohen aktualisht"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Zbatimi i WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 34fd555..970c70f 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивна. Додирните да бисте је активирали."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активна. Додирните да бисте је деактивирали."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Стање приправности апликације: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Подешавања транскодирања медија"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Омогући транскодирање за све апликације"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Онемогућите транскодирање за апликације"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Подешавања транскодирања медија"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Онемогући транскодирање"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Омогућите транскодирање за апликације"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Покренуте услуге"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Приказ и контрола тренутно покренутих услуга"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Примена WebView-а"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 05e455a..42d9077 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Tryck om du vill aktivera."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Tryck om du vill inaktivera."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Status för strömsparfunktion för appar:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Inställningar för medieomkodning"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivera omkodning för alla appar"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Inaktivera omkodning för appar"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Inställningar för medieomkodning"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Inaktivera omkodning"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivera omkodning för appar"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktiva tjänster"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visa och styr aktiva tjänster"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 365b0fd..f1a4199 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Haitumika. Gusa ili ugeuze."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Inatumika. Gusa ili ugeuze."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Hali ya kisitisha programu:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Mipangilio ya kubadilisha muundo wa faili ya maudhui"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Washa ubadilishaji muundo wa faili kwenye programu zote"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Zima ubadilishaji muundo wa faili kwenye programu"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Mipangilio ya kubadilisha muundo wa faili ya maudhui"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Zima ubadilishaji muundo wa faili"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Washa ubadilishaji muundo wa faili kwenye programu"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Huduma zinazoendeshwa"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Onyesha na udhibiti huduma zinazoendeshwa kwa sasa"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Utekelezaji wa WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Imebakisha <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ijae chaji"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index e546ad7..2d696c5 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"செயலில் இல்லை. மாற்ற, தட்டவும்."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"செயலில் உள்ளது. மாற்ற, தட்டவும்."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"காத்திருப்பில் உள்ள ஆப்ஸின் நிலை:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"மீடியா குறிமாற்ற அமைப்புகள்"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"அனைத்து ஆப்ஸுக்கும் குறிமாற்றத்தை இயக்கு"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"அனைத்து ஆப்ஸுக்கும் குறிமாற்றத்தை முடக்கு"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"மீடியா குறிமாற்ற அமைப்புகள்"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"குறிமாற்றத்தை முடக்கு"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ஆப்ஸுக்குக் குறிமாற்றத்தை இயக்கு"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"இயங்கும் சேவைகள்"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"தற்போது இயக்கத்தில் இருக்கும் சேவைகளைப் பார்த்து கட்டுப்படுத்து"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c09d2e1..8e0f0b6 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"నిష్క్రియంగా ఉంది. టోగుల్ చేయడానికి నొక్కండి."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"సక్రియంగా ఉంది. టోగుల్ చేయడానికి నొక్కండి."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"యాప్ స్టాండ్‌బై స్థితి:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"మీడియా ట్రాన్స్‌కోడింగ్ సెట్టింగ్‌లు"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"అన్ని యాప్‌ల కోసం ట్రాన్స్‌కోడింగ్‌ని ఎనేబుల్ చేయండి"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"యాప్‌ల కోసం ట్రాన్స్‌కోడింగ్‌ని డిజేబుల్ చేయండి"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"మీడియా ట్రాన్స్‌కోడింగ్ సెట్టింగ్‌లు"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ట్రాన్స్‌కోడింగ్‌ను డిజేబుల్ చేయండి"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"యాప్‌ల కోసం ట్రాన్స్‌కోడింగ్‌ను ఎనేబుల్ చేయండి"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సేవలు"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సేవలను వీక్షించండి మరియు నియంత్రించండి"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index fcfc11e..81895b9 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ไม่ได้ใช้งาน แตะเพื่อสลับ"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ใช้งานอยู่ แตะเพื่อสลับ"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"สถานะการสแตนด์บายของแอป:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"การตั้งค่าการแปลงสื่อ"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"เปิดใช้การแปลงสำหรับแอปทั้งหมด"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"ปิดใช้การแปลงสำหรับแอป"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"การตั้งค่าการแปลงสื่อ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ปิดใช้การแปลง"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"เปิดใช้การแปลงสำหรับแอป"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"บริการที่ทำงานอยู่"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ดูและควบคุมบริการที่ทำงานอยู่"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"การใช้งาน WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index cdd1d62..5d1630f 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Hindi aktibo. I-tap upang i-toggle."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. I-tap upang i-toggle."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Status ng app standby:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Mga setting ng pagta-transcode ng media"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"I-enable ang pagta-transcode para sa lahat ng app"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"I-disable ang pagta-transcode para sa mga app"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Mga setting ng pag-transcode ng media"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"I-disable ang pag-transcode"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"I-enable ang pag-transcode para sa mga app"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Mga tumatakbong serbisyo"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tingnan at kontrolin ang mga kasalukuyang tumatakbong serbisyo"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Pagpapatupad sa WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 0916d89..34c96a8 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Etkin değil. Geçiş yapmak için dokunun."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Etkin. Geçiş yapmak için dokunun."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Uygulamayı beklemeye alma durumu: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Medya kod dönüştürme ayarları"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Tüm uygulamalar için kod dönüştürmeyi etkinleştirir"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Seçili uygulamalar için kod dönüştürmeyi devre dışı bırakır"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Medya kod dönüştürme ayarları"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Kod dönüştürmeyi devre dışı bırak"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Uygulamalar için kod dönüştürmeyi etkinleştir"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Çalışan hizmetler"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Şu anda çalışan hizmetleri görüntüle ve denetle"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Web Görünümü kullanımı"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 1558ce5..de2456c 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивний додаток. Торкніться, щоб активувати."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активний додаток. Торкніться, щоб дезактивувати."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Режим очікування: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Налаштування перекодування медіаконтенту"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Увімкнути перекодування для всіх додатків"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Вимкнути перекодування для додатків"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Налаштування перекодування медіафайлів"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Вимкнути перекодування"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Увімкнути перекодування додатків"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Запущені сервіси"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Переглянути й налаштувати запущені сервіси"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Застосування WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 507f8cc..7842ba9 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -399,11 +399,11 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیر فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ایپ اسٹینڈ بائی کی حالت:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (6700974145733932357) -->
+    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
     <skip />
-    <!-- no translation found for transcode_enable_all (4719796495995795404) -->
+    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
     <skip />
-    <!-- no translation found for transcode_skip_apps (5680997722349545778) -->
+    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
     <skip />
     <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string>
@@ -452,6 +452,8 @@
     <string name="power_charging" msgid="6727132649743436802">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 84207bf..9457d90 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nofaol. O‘zgartirish uchun bu yerga bosing."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Faol. O‘zgartirish uchun bu yerga bosing."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Kutish rejimi holati: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Media transkripsiya sozlamalari"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Barcha ilovalar transkripsiyasini yoqish"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Barcha ilovalar transkripsiyasini faolsizlantirish"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Media transkripsiyasi sozlamalari"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Transkripsiyani faolsizlantirish"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Ilovalar uchun transkripsiyani yoqish"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Ishlab turgan ilovalar"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ishlab turgan ilovalarni ko‘rish va boshqarish"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ta’minotchisi"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 14a97f3..235987c 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Không hoạt động. Nhấn để chuyển đổi."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Hiện hoạt. Nhấn để chuyển đổi."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Trạng thái chờ ứng dụng:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Tùy chọn chuyển mã ứng dụng"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Bật tùy chọn chuyển mã cho tất cả ứng dụng"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Tắt tùy chọn chuyển mã cho ứng dụng"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Cài đặt chuyển mã nội dung nghe nhìn"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Tắt tùy chọn chuyển mã"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Bật tùy chọn chuyển mã cho ứng dụng"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Các dịch vụ đang chạy"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Xem và kiểm soát các dịch vụ đang chạy"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Triển khai WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc đầy"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc đầy"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 9d312b6..46188e9 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未启用。点按即可切换。"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"已启用。点按即可切换。"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"应用待机状态:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"媒体转码设置"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"为所有应用启用转码"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"为应用停用转码"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"媒体转码设置"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"停用转码功能"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"为应用启用转码功能"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"正在运行的服务"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看和控制当前正在运行的服务"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 实现"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index a8eb246..6f5c025 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕按即可切換。"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕按即可切換。"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"備用應用程式狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"媒體轉碼設定"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"為所有應用程式啟用轉碼功能"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"為所有應用程式停用轉碼功能"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 73692e4..f966df6 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕觸即可切換。"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕觸即可切換。"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"應用程式待命狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"媒體轉碼設定"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"替所有應用程式啟用轉碼功能"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"替應用程式停用轉碼功能"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"正在運作的服務"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並管理目前正在執行的服務"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 實作"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index ead2800..8ad37d3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Akusebenzi. Thepha ukuze ushintshe."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Kuyasebenza. Thepha ukuze ushintshe."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Isimo sokulinda kohlelo lokusebenza:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="6700974145733932357">"Amasethingi emidiya yokudlulisela ikhodi"</string>
-    <string name="transcode_enable_all" msgid="4719796495995795404">"Nikela amandla ukudlulisela ikhodi kuzonke izinhlelo zokusebenza"</string>
-    <string name="transcode_skip_apps" msgid="5680997722349545778">"Khubaza ukudlulisela ikhodi kwezinhlelo zokusebenza"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Amasethingi wemidiya yokudlulisela ikhodi"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Khubaza ukudlulisela ikhodi"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Nika amandla ukudlulisela ikhodi kwezinhlelo zokusebenza"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Amasevisi asebenzayo"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Buka futhi ulawule amasevisi  asebenzayo okwamanje"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"Ukufakwa ke-WebView"</string>
@@ -449,6 +449,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string>
+    <!-- no translation found for power_charging_limited (5902301801611726210) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index cea7903..9f1c96d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1109,6 +1109,8 @@
     <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until charged</string>
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
     <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited -->
+    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string>
 
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
new file mode 100644
index 0000000..1403631
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.settingslib.emergencynumber;
+
+import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.class)
+public class EmergencyNumberUtilsTest {
+    private static final String TELEPHONY_EMERGENCY_NUMBER = "1234";
+    private static final String USER_OVERRIDE_EMERGENCY_NUMBER = "5678";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    private EmergencyNumberUtils mUtils;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+    }
+
+    @Test
+    public void getDefaultPoliceNumber_noTelephony_shouldReturnDefault() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
+        mUtils = new EmergencyNumberUtils(mContext);
+
+        assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(
+                EmergencyNumberUtils.FALL_BACK_NUMBER);
+    }
+
+    @Test
+    public void getDefaultPoliceNumber_hasTelephony_shouldLoadFromTelephony() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        addEmergencyNumberToTelephony();
+        mUtils = new EmergencyNumberUtils(mContext);
+
+
+        assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER);
+    }
+
+    @Test
+    public void getPoliceNumber_hasUserOverride_shouldLoadFromUserOverride() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        addEmergencyNumberToTelephony();
+
+        ContentResolver resolver = RuntimeEnvironment.application.getContentResolver();
+        when(mContext.getContentResolver()).thenReturn(resolver);
+        Settings.Secure.putString(resolver, Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER,
+                USER_OVERRIDE_EMERGENCY_NUMBER);
+
+        mUtils = new EmergencyNumberUtils(mContext);
+
+        assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER);
+    }
+
+    @Test
+    public void getPoliceNumber_noUserOverride_shouldLoadFromTelephony() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        addEmergencyNumberToTelephony();
+
+        mUtils = new EmergencyNumberUtils(mContext);
+
+        assertThat(mUtils.getPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER);
+    }
+
+    private void addEmergencyNumberToTelephony() {
+        final int subId = SubscriptionManager.getDefaultSubscriptionId();
+        EmergencyNumber emergencyNumber = mock(EmergencyNumber.class);
+        Map<Integer, List<EmergencyNumber>> numbers = new ArrayMap<>();
+        List<EmergencyNumber> numbersForSubId = new ArrayList<>();
+        numbersForSubId.add(emergencyNumber);
+        numbers.put(subId, numbersForSubId);
+        when(mTelephonyManager.getEmergencyNumberList(
+                EMERGENCY_SERVICE_CATEGORY_POLICE)).thenReturn(numbers);
+        when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER);
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d16aebb..9331b5e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -269,6 +269,7 @@
         VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.EMERGENCY_GESTURE_CALL_NUMBER, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a3bda96..af12ddd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -315,7 +315,6 @@
                     Settings.Global.KERNEL_CPU_THREAD_READER,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
                     Settings.Global.LANG_ID_UPDATE_METADATA_URL,
-                    Settings.Global.LATENCY_TRACKER,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -743,6 +742,7 @@
                  Settings.Secure.SKIP_GESTURE,
                  Settings.Secure.SILENCE_GESTURE,
                  Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                 Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER,
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL,
                  Settings.Secure.TAP_GESTURE,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3de0fbd..7120cc2 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -623,8 +623,7 @@
 
         <service
             android:name=".keyguard.KeyguardService"
-            android:exported="true"
-            android:enabled="@bool/config_enableKeyguardService" />
+            android:exported="true" />
 
         <activity android:name=".keyguard.WorkLockActivity"
                   android:label="@string/accessibility_desc_work_lock"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 63f8b1f..0f94bca 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -21,6 +21,7 @@
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -30,7 +31,7 @@
  */
 @ProvidesInterface(version = FalsingManager.VERSION)
 public interface FalsingManager {
-    int VERSION = 5;
+    int VERSION = 6;
 
     void onSuccessfulUnlock();
 
@@ -45,6 +46,42 @@
     /** Returns true if the gesture should be rejected. */
     boolean isFalseTouch(int interactionType);
 
+    /**
+     * Returns true if the FalsingManager thinks the last gesure was not a valid tap.
+     *
+     * Accepts one parameter, robustCheck, that distinctly changes behavior. When set to false,
+     * this method simply looks at the last gesture and returns whether it is a tap or not, (as
+     * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
+     * is performed that can include historical interactions and other contextual cues to see
+     * if the tap looks accidental.
+     *
+     * Set robustCheck to true if you want to validate a tap for launching an action, like opening
+     * a notification. Set to false if you simply want to know if the last gesture looked like a
+     * tap.
+     */
+    boolean isFalseTap(boolean robustCheck);
+
+    /**
+     * Returns true if the last two gestures do not look like a double tap.
+     *
+     * Only works on data that has already been reported to the FalsingManager. Be sure that
+     * {@link #onTouchEvent(MotionEvent, int, int)} has already been called for all of the
+     * taps you want considered.
+     *
+     * This looks at the last two gestures on the screen, ensuring that they meet the following
+     * criteria:
+     *
+     *   a) There are at least two gestures.
+     *   b) The last two gestures look like taps.
+     *   c) The last two gestures look like a double tap taken together.
+     *
+     *   This method is _not_ context aware. That is to say, if two taps occur on two neighboring
+     *   views, but are otherwise close to one another, this will report a successful double tap.
+     *   It is up to the caller to decide
+     * @return
+     */
+    boolean isFalseDoubleTap();
+
     void onNotificatonStopDraggingDown();
 
     void setNotificationExpanded();
@@ -103,7 +140,8 @@
 
     void onTouchEvent(MotionEvent ev, int width, int height);
 
-    void dump(PrintWriter pw);
+    /** From com.android.systemui.Dumpable. */
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
     void cleanup();
 }
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 7816d62..b75c2c4 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -82,6 +82,28 @@
             android:elegantTextHeight="false"
         />
     </FrameLayout>
+    <FrameLayout
+        android:id="@+id/new_lockscreen_clock_view_large"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/keyguard_status_area"
+        android:paddingTop="20dp"
+        android:visibility="gone">
+        <com.android.keyguard.AnimatableClockView
+            android:id="@+id/animatable_clock_view_large"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:gravity="center_horizontal"
+            android:textSize="170dp"
+            android:letterSpacing="0.02"
+            android:lineSpacingMultiplier=".8"
+            android:includeFontPadding="false"
+            android:fontFamily="@font/clock"
+            android:typeface="monospace"
+            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-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 92dd9fd..276fa23 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans vinnig"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Koppel jou laaier."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk Kieslys om te ontsluit."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk is gesluit"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index f94c20f..ae5d1f6 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ኃይል መሙያዎን ያያይዙ።"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመክፈት ምናሌ ተጫን።"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 65e3f0d..fb03fe3 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"توصيل جهاز الشحن."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 3b51e48..8e8b13d 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চ্চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চ্চাৰ্জ কৰি থকা হৈছে"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"আপোনাৰ চ্চার্জাৰ সংযোগ কৰক।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক কৰিবলৈ মেনু টিপক।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱর্ক লক কৰা অৱস্থাত আছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index ea07c3d..f9bff73 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Adapteri qoşun."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Şəbəkə kilidlidir"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index e206958..3434ac6 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 569e705..07d682f 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Падключыце зарадную прыладу."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Націсніце кнопку \"Меню\", каб разблакіраваць."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index d015be3..3890c11 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Свържете зарядното си устройство."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натиснете „Меню“, за да отключите."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 8eae6e6..f916374 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"আপনার চার্জার সংযুক্ত করুন।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক করতে মেনুতে টিপুন।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ার্ক লক করা আছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 286b08b..59a881d 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo punjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite meni da otključate."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index cb7fa37..82d63c1 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connecta el carregador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 4f0c0ff..a4e653f 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rychlé nabíjení"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabíjení"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Připojte dobíjecí zařízení."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Klávesy odemknete stisknutím tlačítka nabídky."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Síť je blokována"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index e486fc6..604852c 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Tilslut din oplader."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tryk på menuen for at låse op."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er låst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 06d012f..0e2ea13 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Ladegerät anschließen."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 1764284..90e2550 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Φόρτιση"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Γρήγορη φόρτιση"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Αργή φόρτιση"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Συνδέστε τον φορτιστή."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Κλειδωμένο δίκτυο"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 92a1594..7b0c638 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 719f1a1..2ef720e 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 92a1594..7b0c638 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 92a1594..7b0c638 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 975b1f6..471ef8b 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging rapidly‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging slowly‎‏‎‎‏‎"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Battery limited temporarily‎‏‎‎‏‎"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‏‎‎‎‎Connect your charger.‎‏‎‎‏‎"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎Press Menu to unlock.‎‏‎‎‏‎"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎Network locked‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 25ab615..a5750b5 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta tu cargador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Presiona Menú para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 0754681..69488da 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta el cargador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pulsa el menú para desbloquear la pantalla."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 331a95c..948e134 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Ühendage laadija."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Vajutage avamiseks menüüklahvi."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 3ff224b..b1aa206 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Konektatu kargagailua."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 5e69636..8fecdd3 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهسته‌آهسته شارژ می‌شود"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"شارژر را وصل کنید."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"برای باز کردن قفل روی «منو» فشار دهید."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"شبکه قفل شد"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 54bc4d8..c47de5d 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Kytke laturi."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Poista lukitus painamalla Valikkoa."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 2eafc2f..b330452 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 824ea41..0c13c0f 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente…"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 0948871..601b2e2 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta o cargador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index b02d3d9..296124f 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"તમારું ચાર્જર કનેક્ટ કરો."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index f6b15de..c1c9dd5 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज हो रहा है"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तेज़ चार्ज हो रहा है"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चार्ज हो रहा है"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"अपना चार्जर कनेक्‍ट करें."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक किया हुआ है"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 49db3f88..1b51b33 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • punjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brzo punjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Izbornik da biste otključali."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index c26998f..4dcb229 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Töltés"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gyors töltés"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Csatlakoztassa a töltőt."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"A feloldáshoz nyomja meg a Menü gombot."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Hálózat zárolva"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index ad949d4..354d932 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Միացրեք լիցքավորիչը:"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 85b2a47..d43823c 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Hubungkan pengisi daya."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index e40cdca..233402b 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Tengdu hleðslutækið."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index e1c9ee8..87fd81c 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batteria momentaneamente limitata"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Collega il caricabatterie."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index e054f62..6af70b4 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"חבר את המטען."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 957d78a..ab5a13c 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"充電してください。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"メニューからロックを解除できます。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ネットワークがロックされました"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index d0d15fe..b10ab61 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • სწრაფად იტენება"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელა იტენება"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ბატარეა დროებით შეზღუდულია"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"შეაერთეთ დამტენი."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"განსაბლოკად დააჭირეთ მენიუს."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ქსელი ჩაკეტილია"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 62afd1e..c415bbb 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Зарядтағышты қосыңыз."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 52b7fab..1a278e8 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្ម"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយឺត"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"សូមសាក​ថ្ម​របស់​អ្នក។"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ​ដើម្បី​ដោះ​សោ។"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញ​ជាប់​សោ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 785ca43..961fcec 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್‌ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ನಿಮ್ಮ ಚಾರ್ಜರ್ ಸಂಪರ್ಕಗೊಳಿಸಿ."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್‌ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 848490e..2f93af9 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 중"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"충전기를 연결하세요."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"잠금 해제하려면 메뉴를 누르세요."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"네트워크 잠김"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index d868788..7e92919 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубатталууда"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Тез кубатталууда"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Кубаттагычка туташтырыңыз."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Кулпуну ачуу үчүн Менюну басыңыз."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Тармак кулпуланган"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index ebaffb1..b96a2bb 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ເຊື່ອມຕໍ່ສາຍສາກຂອງທ່ານ."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືອຂ່າຍຖືກລັອກ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 4d598f6..773f7c4 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkraunama"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Greitai įkraunama"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lėtai įkraunama"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Prijunkite kroviklį."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Paspauskite meniu, jei norite atrakinti."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tinklas užrakintas"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index fad67d5..68bf4cd 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Pievienojiet uzlādes ierīci."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lai atbloķētu, nospiediet izvēlnes ikonu."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tīkls ir bloķēts."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 1397f46..e51f774 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Поврзете го полначот."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притиснете „Мени“ за отклучување."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index f82f822..4b43914 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജ് ചെയ്യുന്നു"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"നിങ്ങളുടെ ചാർജർ കണക്റ്റുചെയ്യുക."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"നെറ്റ്‌വർക്ക് ലോക്കുചെയ്‌തു"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 462017a..4667252 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Цэнэглэгчээ холбоно уу."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 0166791..bdd3904 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"तुमचा चार्जर कनेक्ट करा."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलॉक करण्यासाठी मेनू दाबा."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक केले"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 6750086..a517988 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Sambungkan pengecas anda."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 3b32f06..07f3d38 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းနေသည်"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အမြန်အားသွင်းနေသည်"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"အားသွင်းကိရိယာကို ချိတ်ဆက်ပါ။"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"မီနူးကို နှိပ်၍ လော့ခ်ဖွင့်ပါ။"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ကွန်ရက်ကို လော့ခ်ချထားသည်"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index ebd8f29..a391b92 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader raskt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Koble til en batterilader."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Trykk på menyknappen for å låse opp."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nettverket er låst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index ce05e38..812aa38 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"तपाईंको चार्जर जोड्नुहोस्।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लक भएको छ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index aa783e8..f1c2033 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Snel opladen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Sluit de oplader aan."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk op Menu om te ontgrendelen."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk vergrendeld"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index 8bbdcf1..be6e285 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହେଉଛି"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଦ୍ରୁତ ଭାବେ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ଆପଣଙ୍କ ଚାର୍ଜର୍‍ ସଂଯୋଗ କରନ୍ତୁ।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ନେଟୱର୍କକୁ ଲକ୍‌ କରାଯାଇଛି"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 78e0665..a6ea6aa 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ਆਪਣਾ ਚਾਰਜਰ ਕਨੈਕਟ ਕਰੋ।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ  ਲਾਕ  ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 5094cf9..9c6f271 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Podłącz ładowarkę."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 5bfc3db..d2d5fb9 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecte o seu carregador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 5af8bc0..7f0a4d88 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar…"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar rapidamente…"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente."</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Ligue o carregador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prima Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 5bfc3db..d2d5fb9 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -38,6 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecte o seu carregador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 8122241..572c1db 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conectați încărcătorul."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apăsați pe Meniu pentru a debloca."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rețea blocată"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index b80b479..2c87608 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Подключите зарядное устройство."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Для разблокировки нажмите \"Меню\"."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 1cd876f..8682192 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ඔබගේ ආරෝපකයට සම්බන්ධ කරන්න."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 801a7db..d5e6093 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa rýchlo"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa pomaly"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Pripojte nabíjačku."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Odomknete stlačením tlačidla ponuky."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieť je zablokovaná"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 967255c..82d5c54 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite napajalnik."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 382a4dc..a67413fd 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me shpejtësi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Lidh karikuesin."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Shtyp \"Meny\" për të shkyçur."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index f83df3f..dec3165 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Прикључите пуњач."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index a037bff..3ef2dfe 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Anslut laddaren."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lås upp genom att trycka på Meny."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk låst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index efa5ecf..5c5fc10 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Unganisha chaja yako."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Bonyeza Menyu ili kufungua."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 96dbbb0..c188301 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"சார்ஜரை இணைக்கவும்."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"திறக்க, மெனுவை அழுத்தவும்."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெட்வொர்க் பூட்டப்பட்டது"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index d44003b..27a5b28 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"మీ ఛార్జర్‌ను కనెక్ట్ చేయండి."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్‌లాక్ చేయడానికి మెనుని నొక్కండి."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్‌వర్క్ లాక్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index e157be4..e506745 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างเร็ว"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างช้าๆ"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"เสียบที่ชาร์จของคุณ"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"กด \"เมนู\" เพื่อปลดล็อก"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"เครือข่ายถูกล็อก"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index 7b7e17d..912fd95 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nagcha-charge"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabilis na nagcha-charge"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Ikonekta ang iyong charger."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pindutin ang Menu upang i-unlock."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 8c0caea..5390c4c 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Şarj cihazınızı takın."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmak için Menü\'ye basın."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ağ kilitli"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 6e5ce0f..6263d79 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Підключіть зарядний пристрій."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 0fd5e17..bf936cb 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"اپنا چارجر منسلک کریں۔"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقفل ہو گیا"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 323fea5..0b23e16 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Quvvatlash moslamasini ulang."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 2ba5089..9357eba 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Kết nối bộ sạc của bạn."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index b4bff5f..96728ab 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充电"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充电"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在慢速充电"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"请连接充电器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按“菜单”即可解锁。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"网络已锁定"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index b3d3877..c683529 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> •正在慢速充電"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [選單] 即可解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 03dec48..7a3b5c5 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 5ab567f..ec38cbe 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -38,6 +38,8 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kaningi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string>
+    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
+    <skip />
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Xhuma ishaja yakho."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Chofoza Menyu ukuvula."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Inethiwekhi ivaliwe"</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index f7e9fed..d95ab37 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -79,6 +79,9 @@
          is not fully charged, and it's plugged into a slow charger, say that it's charging slowly.  -->
     <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
 
+    <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily.  -->
+    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string>
+
     <!-- When the lock screen is showing and the battery is low, warn user to plug
          in the phone soon. -->
     <string name="keyguard_low_battery">Connect your charger.</string>
diff --git a/packages/SystemUI/res-product/values-de/strings.xml b/packages/SystemUI/res-product/values-de/strings.xml
index 5c8f842..a84413d 100644
--- a/packages/SystemUI/res-product/values-de/strings.xml
+++ b/packages/SystemUI/res-product/values-de/strings.xml
@@ -21,7 +21,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Smartphone genau platzieren, um es schneller zu laden"</string>
     <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Smartphone genau platzieren, um es kabellos zu laden"</string>
-    <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Das Android TV-Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke beispielsweise eine Taste oder berühre den Bildschirm."</string>
+    <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Das Android TV-Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke eine Taste."</string>
     <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Das Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke beispielsweise eine Taste oder berühre den Bildschirm."</string>
     <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Keine SIM-Karte im Tablet."</string>
     <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Keine SIM-Karte im Smartphone."</string>
diff --git a/packages/SystemUI/res-product/values-pl/strings.xml b/packages/SystemUI/res-product/values-pl/strings.xml
index 9a9980a..5ac3d84 100644
--- a/packages/SystemUI/res-product/values-pl/strings.xml
+++ b/packages/SystemUI/res-product/values-pl/strings.xml
@@ -34,10 +34,10 @@
     <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
     <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
     <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowano wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowano wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Odblokuj telefon, by wyświetlić więcej opcji"</string>
diff --git a/packages/SystemUI/res/layout/controls_dialog_pin.xml b/packages/SystemUI/res/layout/controls_dialog_pin.xml
index 170b32b..d0ef10b 100644
--- a/packages/SystemUI/res/layout/controls_dialog_pin.xml
+++ b/packages/SystemUI/res/layout/controls_dialog_pin.xml
@@ -27,6 +27,7 @@
       android:layout_height="wrap_content"
       android:minHeight="48dp"
       android:longClickable="false"
+      android:textAlignment="viewStart"
       android:inputType="numberPassword" />
   <CheckBox
       android:id="@+id/controls_pin_use_alpha"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 0ba546e..f984100 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -78,7 +78,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/status_bar_height"
-        android:layout_gravity="top|center_horizontal">
+        android:layout_gravity="top|center_horizontal"
+        android:gravity="center_horizontal">
         <com.android.systemui.statusbar.phone.LockIcon
             android:id="@+id/lock_icon"
             android:layout_width="@dimen/keyguard_lock_width"
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8d86478..6b598da 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -944,7 +944,7 @@
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"Application ouverte sans avoir été installée."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Application ouverte sans avoir été installée. Touchez ici pour en savoir plus."</string>
-    <string name="app_info" msgid="5153758994129963243">"Détails de l\'applic."</string>
+    <string name="app_info" msgid="5153758994129963243">"Détails de l\'appli"</string>
     <string name="go_to_web" msgid="636673528981366511">"Ouvrir le navigateur"</string>
     <string name="mobile_data" msgid="4564407557775397216">"Données cellulaires"</string>
     <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 160e05d..df86e8a 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -33,8 +33,8 @@
     <string name="invalid_charger_title" msgid="938685362320735167">"USB арқылы зарядтау мүмкін емес"</string>
     <string name="invalid_charger_text" msgid="2339310107232691577">"Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз"</string>
     <string name="battery_low_why" msgid="2056750982959359863">"Параметрлер"</string>
-    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Battery Saver функциясын қосу керек пе?"</string>
-    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Battery Saver туралы ақпарат"</string>
+    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Батареяны үнемдеу режимін қосу керек пе?"</string>
+    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Батареяны үнемдеу режимі туралы ақпарат"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Қосу"</string>
     <string name="battery_saver_start_action" msgid="4553256017945469937">"Battery saver функциясын қосу"</string>
     <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Параметрлер"</string>
@@ -501,7 +501,7 @@
     <string name="user_remove_user_title" msgid="9124124694835811874">"Пайдаланушы жойылсын ба?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Осы пайдаланушының барлық қолданбалары мен деректері жойылады."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Жою"</string>
-    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery saver қосулы"</string>
+    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Батареяны үнемдеу режимі қосулы"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Өнімділікті және фондық деректерді азайтады"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Battery saver функциясын өшіру"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жазу не трансляциялау кезінде экранда көрсетілетін немесе дыбысталатын барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string>
@@ -777,7 +777,7 @@
       <item quantity="one">%d минут</item>
     </plurals>
     <string name="battery_panel_title" msgid="5931157246673665963">"Батареяны пайдалану"</string>
-    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
+    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батареяны үнемдеу режимі істемейді"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Өнімділікті және фондық деректерді азайтады"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string>
@@ -965,7 +965,7 @@
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына кез келген қолданбаның үзіндісін көрсетуге рұқсат беру"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"Рұқсат беру"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"Тыйым салу"</string>
-    <string name="auto_saver_title" msgid="6873691178754086596">"Түймені түртіп, Battery Saver функциясын реттеңіз"</string>
+    <string name="auto_saver_title" msgid="6873691178754086596">"Түймені түртіп, Батареяны үнемдеу режимін реттеңіз"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Батареяның заряды бітуге жақындағанда қосыңыз."</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Жоқ, рақмет"</string>
     <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Battery Saver кестесі қосылды"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index b841fa95..0d605c1 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -508,7 +508,7 @@
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Энэ функцийг ажиллуулж байгаа үйлчилгээ нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Бичлэг хийх эсвэл дамжуулахыг эхлүүлэх үү?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
-    <string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж үл харуулах"</string>
+    <string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж бүү харуул"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string>
@@ -874,12 +874,12 @@
   <string-array name="clock_options">
     <item msgid="3986445361435142273">"Цаг, минут, секундийг харуулах"</item>
     <item msgid="1271006222031257266">"Цаг, минутыг харуулах (өгөгдмөл)"</item>
-    <item msgid="6135970080453877218">"Энэ дүрс тэмдгийг бүү үзүүл"</item>
+    <item msgid="6135970080453877218">"Энэ дүрс тэмдгийг бүү харуул"</item>
   </string-array>
   <string-array name="battery_options">
     <item msgid="7714004721411852551">"Хувийг тогтмол харуулах"</item>
     <item msgid="3805744470661798712">"Цэнэглэх үед хувийг тогтмол харуулах (өгөгдмөл)"</item>
-    <item msgid="8619482474544321778">"Энэ дүрс тэмдгийг бүү үзүүл"</item>
+    <item msgid="8619482474544321778">"Энэ дүрс тэмдгийг бүү харуул"</item>
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
     <string name="other" msgid="429768510980739978">"Бусад"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 621c497..881be66 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -692,7 +692,7 @@
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"सशक्त सूचना नियन्त्रण"</string>
     <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"अन छ"</string>
     <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"अफ"</string>
-    <string name="power_notification_controls_description" msgid="1334963837572708952">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string>
+    <string name="power_notification_controls_description" msgid="1334963837572708952">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- एपका सबै सूचनाहरूलाई रोक्ने"</string>
     <string name="notification_header_default_channel" msgid="225454696914642444">"सूचनाहरू"</string>
     <string name="notification_channel_disabled" msgid="928065923928416337">"तपाईं अब उप्रान्त यी सूचनाहरू देख्नु हुने छैन"</string>
     <string name="notification_channel_minimized" msgid="6892672757877552959">"यी सूचनाहरू सानो बनाइने छ"</string>
@@ -712,7 +712,7 @@
     <string name="inline_silent_button_alert" msgid="5705343216858250354">"सतर्क गराउने"</string>
     <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"सर्तक गराइरहनुहोस्"</string>
     <string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाहरू निष्क्रिय पार्नुहोस्"</string>
-    <string name="inline_keep_showing_app" msgid="4393429060390649757">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
+    <string name="inline_keep_showing_app" msgid="4393429060390649757">"यो एपका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
     <string name="notification_silence_title" msgid="8608090968400832335">"मौन"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"पूर्वनिर्धारित"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 1c6b6a1..3e944b5 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -538,8 +538,8 @@
     <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"To urządzenie należy do Twojej organizacji i jest połączone z sieciami VPN"</string>
     <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i jest połączone z sieciami VPN"</string>
-    <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Twoja organizacja może monitorować ruch w sieci w Twoim profilu do pracy"</string>
-    <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> może monitorować ruch w sieci w Twoim profilu do pracy"</string>
+    <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Twoja organizacja może monitorować ruch w sieci w Twoim profilu służbowym"</string>
+    <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> może monitorować ruch w sieci w Twoim profilu służbowym"</string>
     <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Sieć może być monitorowana"</string>
     <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"To urządzenie jest połączone z sieciami VPN"</string>
     <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Twój profil służbowy jest połączony z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
@@ -558,7 +558,7 @@
     <string name="monitoring_description_named_management" msgid="505833016545056036">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string>
     <string name="monitoring_description_management" msgid="4308879039175729014">"To urządzenie należy do Twojej organizacji.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Twoja organizacja zainstalowała urząd certyfikacji na tym urządzeniu. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
-    <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu do pracy. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
+    <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu służbowym. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Urząd certyfikacji zainstalowany na tym urządzeniu. Twój zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu."</string>
     <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Łączysz się z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
@@ -577,14 +577,14 @@
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Otwórz zaufane certyfikaty"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem."</string>
     <string name="monitoring_description_vpn" msgid="1685428000684586870">"Aplikacja otrzymała od Ciebie uprawnienia do konfigurowania połączenia VPN.\n\nMoże ona monitorować Twoją aktywność na urządzeniu i w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"Twoim profilem do pracy zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem.\n\nŁączysz się też z siecią VPN, która może monitorować Twoją aktywność w sieci."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"Twoim profilem służbowym zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem.\n\nŁączysz się też z siecią VPN, która może monitorować Twoją aktywność w sieci."</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tym urządzeniem zarządza Twój rodzic. Rodzic może zobaczyć różne informacje, np. o aplikacjach, których używasz, lokalizacji i czasie korzystania z urządzenia, a także zarządzać tymi danymi."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="monitoring_description_app" msgid="376868879287922929">"Łączysz się z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
     <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
     <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
-    <string name="monitoring_description_app_work" msgid="3713084153786663662">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem do pracy. Profil jest połączony z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nSkontaktuj się z administratorem, aby uzyskać więcej informacji."</string>
-    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem do pracy. Profil jest połączony z aplikacją <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nMasz też połączenie z aplikacją <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, która może monitorować Twoją osobistą aktywność w sieci."</string>
+    <string name="monitoring_description_app_work" msgid="3713084153786663662">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem służbowym. Profil jest połączony z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nSkontaktuj się z administratorem, aby uzyskać więcej informacji."</string>
+    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem służbowym. Profil jest połączony z aplikacją <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nMasz też połączenie z aplikacją <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, która może monitorować Twoją osobistą aktywność w sieci."</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Blokada anulowana przez agenta zaufania"</string>
     <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"Urządzenie pozostanie zablokowane, aż odblokujesz je ręcznie"</string>
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index cebe1e8..09ec439 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -44,6 +44,9 @@
         <item>com.android.systemui.wmshell.WMShell</item>
     </string-array>
 
+    <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
+    <integer name="recents_svelte_level">3</integer>
+
     <!-- Show a separate icon for low and high volume on the volume dialog -->
     <bool name="config_showLowMediaVolumeIcon">true</bool>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4407d8f..880dd378 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -161,9 +161,6 @@
     <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
     <integer name="ambient_notification_extension_time">10000</integer>
 
-    <!-- Whether to enable KeyguardService or not -->
-    <bool name="config_enableKeyguardService">true</bool>
-
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
      card. -->
     <integer name="keyguard_max_notification_count">3</integer>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2839189..f38e653 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -305,6 +305,9 @@
     <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
     </style>
 
+    <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod">
+    </style>
+
     <!-- Standard animations for hiding and showing the status bar. -->
     <style name="Animation.StatusBar">
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 3de0b4b..d8fa90a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.util.Log;
+import android.view.WindowInsetsController.Appearance;
 
 /**
  * Data for a single thumbnail.
@@ -43,7 +44,7 @@
     public boolean isRealSnapshot;
     public boolean isTranslucent;
     public int windowingMode;
-    public int systemUiVisibility;
+    public @Appearance int appearance;
     public float scale;
     public long snapshotId;
 
@@ -57,7 +58,6 @@
         isRealSnapshot = true;
         isTranslucent = false;
         windowingMode = WINDOWING_MODE_UNDEFINED;
-        systemUiVisibility = 0;
         snapshotId = 0;
     }
 
@@ -93,7 +93,7 @@
         isRealSnapshot = snapshot.isRealSnapshot();
         isTranslucent = snapshot.isTranslucent();
         windowingMode = snapshot.getWindowingMode();
-        systemUiVisibility = snapshot.getSystemUiVisibility();
+        appearance = snapshot.getAppearance();
         snapshotId = snapshot.getId();
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
new file mode 100644
index 0000000..4a870f1
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
@@ -0,0 +1,172 @@
+/*
+ * 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.shared.system;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+
+import java.util.HashMap;
+
+public class ViewTreeObserverWrapper {
+
+    private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
+            sOnComputeInsetsListenerMap = new HashMap<>();
+
+    /**
+     * Register a callback to be invoked when the invoked when it is time to
+     * compute the window's insets.
+     *
+     * @param listener The callback to add
+     * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
+     */
+    public static void addOnComputeInsetsListener(
+            ViewTreeObserver observer, OnComputeInsetsListener listener) {
+        final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
+            final InsetsInfo inOutInfo = new InsetsInfo();
+            inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
+            inOutInfo.visibleInsets.set(internalInOutInfo.visibleInsets);
+            inOutInfo.touchableRegion.set(internalInOutInfo.touchableRegion);
+            listener.onComputeInsets(inOutInfo);
+            internalInOutInfo.contentInsets.set(inOutInfo.contentInsets);
+            internalInOutInfo.visibleInsets.set(inOutInfo.visibleInsets);
+            internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
+            internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
+        };
+        sOnComputeInsetsListenerMap.put(listener, internalListener);
+        observer.addOnComputeInternalInsetsListener(internalListener);
+    }
+
+    /**
+     * Remove a previously installed insets computation callback
+     *
+     * @param victim The callback to remove
+     * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
+     * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
+     */
+    public void removeOnComputeInsetsListener(
+            ViewTreeObserver observer, OnComputeInsetsListener victim) {
+        final OnComputeInternalInsetsListener listener = sOnComputeInsetsListenerMap.get(victim);
+        if (listener != null) {
+            observer.removeOnComputeInternalInsetsListener(listener);
+        }
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when layout has
+     * completed and the client can compute its interior insets.
+     */
+    public interface OnComputeInsetsListener {
+        /**
+         * Callback method to be invoked when layout has completed and the
+         * client can compute its interior insets.
+         *
+         * @param inoutInfo Should be filled in by the implementation with
+         * the information about the insets of the window.  This is called
+         * with whatever values the previous OnComputeInsetsListener
+         * returned, if there are multiple such listeners in the window.
+         */
+        void onComputeInsets(InsetsInfo inoutInfo);
+    }
+
+    /**
+     * Parameters used with OnComputeInsetsListener.
+     */
+    public final static class InsetsInfo {
+
+        /**
+         * Offsets from the frame of the window at which the content of
+         * windows behind it should be placed.
+         */
+        public final Rect contentInsets = new Rect();
+
+        /**
+         * Offsets from the frame of the window at which windows behind it
+         * are visible.
+         */
+        public final Rect visibleInsets = new Rect();
+
+        /**
+         * Touchable region defined relative to the origin of the frame of the window.
+         * Only used when {@link #setTouchableInsets(int)} is called with
+         * the option {@link #TOUCHABLE_INSETS_REGION}.
+         */
+        public final Region touchableRegion = new Region();
+
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the entire window frame
+         * can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_FRAME =
+                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the content insets can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_CONTENT =
+                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the visible insets can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_VISIBLE =
+                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the provided touchable region in {@link #touchableRegion} can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_REGION =
+                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+        /**
+         * Set which parts of the window can be touched: either
+         * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
+         * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
+         */
+        public void setTouchableInsets(int val) {
+            mTouchableInsets = val;
+        }
+
+        int mTouchableInsets;
+
+        @Override
+        public int hashCode() {
+            int result = contentInsets.hashCode();
+            result = 31 * result + visibleInsets.hashCode();
+            result = 31 * result + touchableRegion.hashCode();
+            result = 31 * result + mTouchableInsets;
+            return result;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            final InsetsInfo other = (InsetsInfo) o;
+            return mTouchableInsets == other.mTouchableInsets &&
+                    contentInsets.equals(other.contentInsets) &&
+                    visibleInsets.equals(other.visibleInsets) &&
+                    touchableRegion.equals(other.touchableRegion);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index d731840..68405a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -76,6 +76,7 @@
      * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
      */
     private FrameLayout mNewLockscreenClockFrame;
+    private FrameLayout mNewLockscreenLargeClockFrame;
 
     /**
      * Frame for default and custom clock.
@@ -154,14 +155,12 @@
             mNewLockscreenClockFrame.setVisibility(VISIBLE);
 
             statusAreaLP.removeRule(RelativeLayout.BELOW);
-            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view);
             statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
         } else {
             setPaddingRelative(0, 0, 0, 0);
             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);
         }
@@ -175,6 +174,7 @@
         mClockView = findViewById(R.id.default_clock_view);
         mClockViewBold = findViewById(R.id.default_clock_view_bold);
         mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view);
+        mNewLockscreenLargeClockFrame = findViewById(R.id.new_lockscreen_clock_view_large);
         mSmallClockFrame = findViewById(R.id.clock_view);
         mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
@@ -292,6 +292,33 @@
         mClockViewBold.setFormat24Hour(format);
     }
 
+    private void updateClockLayout(boolean useLargeClock) {
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) return;
+
+        Fade fadeIn = new Fade();
+        fadeIn.setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION);
+        fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+
+        Fade fadeOut = new Fade();
+        fadeOut.setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
+        fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+
+        if (useLargeClock) {
+            TransitionManager.beginDelayedTransition(mNewLockscreenClockFrame, fadeOut);
+            TransitionManager.beginDelayedTransition(mNewLockscreenLargeClockFrame, fadeIn);
+
+            mNewLockscreenClockFrame.setVisibility(View.INVISIBLE);
+            addView(mNewLockscreenLargeClockFrame);
+            mNewLockscreenLargeClockFrame.setVisibility(View.VISIBLE);
+        } else {
+            TransitionManager.beginDelayedTransition(mNewLockscreenClockFrame, fadeIn);
+            TransitionManager.beginDelayedTransition(mNewLockscreenLargeClockFrame, fadeOut);
+
+            removeView(mNewLockscreenLargeClockFrame);
+            mNewLockscreenClockFrame.setVisibility(View.VISIBLE);
+        }
+    }
+
     /**
      * Set the amount (ratio) that the device has transitioned to doze.
      *
@@ -312,6 +339,8 @@
         if (hasVisibleNotifications == mHasVisibleNotifications) {
             return;
         }
+        updateClockLayout(!hasVisibleNotifications);
+
         mHasVisibleNotifications = hasVisibleNotifications;
         if (mDarkAmount == 0f && mBigClockContainer != null) {
             // Starting a fade transition since the visibility of the big clock will change.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 6c69534..c5c36e9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -61,6 +61,7 @@
      */
     private AnimatableClockController mNewLockScreenClockViewController;
     private FrameLayout mNewLockScreenClockFrame;
+    private AnimatableClockController mNewLockScreenLargeClockViewController;
 
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
@@ -189,6 +190,7 @@
     void refresh() {
         if (mNewLockScreenClockViewController != null) {
             mNewLockScreenClockViewController.refreshTime();
+            mNewLockScreenLargeClockViewController.refreshTime();
         }
 
         mView.refresh();
@@ -221,9 +223,15 @@
                                 mView.findViewById(R.id.animatable_clock_view),
                                 mStatusBarStateController);
                 mNewLockScreenClockViewController.init();
+                mNewLockScreenLargeClockViewController =
+                        new AnimatableClockController(
+                                mView.findViewById(R.id.animatable_clock_view_large),
+                                mStatusBarStateController);
+                mNewLockScreenLargeClockViewController.init();
             }
         } else {
             mNewLockScreenClockViewController = null;
+            mNewLockScreenLargeClockViewController = null;
         }
         mView.updateLockScreenMode(mLockScreenMode);
         updateAodIcons();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 2036b33..97aa26f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -178,6 +178,13 @@
         return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0;
     }
 
+    int getOwnerInfoHeight() {
+        if (mOwnerInfo == null) {
+            return 0;
+        }
+        return mOwnerInfo.getVisibility() == VISIBLE ? mOwnerInfo.getHeight() : 0;
+    }
+
     void updateLogoutView() {
         if (mLogoutView == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index dcb306e..c0e06e8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -130,6 +130,13 @@
     }
 
     /**
+     * Get the height of the logout button.
+     */
+    public int getOwnerInfoHeight() {
+        return mView.getOwnerInfoHeight();
+    }
+
+    /**
      * Set keyguard status view alpha.
      */
     public void setAlpha(float alpha) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 767afa7..611131f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1861,7 +1861,6 @@
             @Override
             public void onChange(boolean selfChange) {
                 updateLockScreenMode();
-                mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
             }
         };
         mContext.getContentResolver().registerContentObserver(
@@ -1870,14 +1869,22 @@
     }
 
     private void updateLockScreenMode() {
-        mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(),
+        final int newMode = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.SHOW_NEW_LOCKSCREEN,
                 isUdfpsEnrolled() ? 1 : 0);
+        if (newMode != mLockScreenMode) {
+            mLockScreenMode = newMode;
+            mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
+        }
     }
 
     private void updateUdfpsEnrolled(int userId) {
         mIsUdfpsEnrolled = mAuthController.isUdfpsEnrolled(userId);
     }
+
+    /**
+     * @return true if there's at least one udfps enrolled
+     */
     public boolean isUdfpsEnrolled() {
         return mIsUdfpsEnrolled;
     }
@@ -1917,6 +1924,7 @@
             return;
         }
 
+        // TODO: Add support for multiple fingerprint sensors, b/173730729
         boolean shouldListenForFingerprint =
                 isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint();
         boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8aa3493c..6151ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -24,6 +24,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -78,9 +79,9 @@
 
     private float mInitialTouchPos;
     private float mPerpendicularInitialTouchPos;
-    private boolean mDragging;
+    private boolean mIsSwiping;
     private boolean mSnappingChild;
-    private View mCurrView;
+    private View mTouchedView;
     private boolean mCanCurrViewBeDimissed;
     private float mDensityScale;
     private float mTranslation = 0;
@@ -95,14 +96,14 @@
 
         @Override
         public void run() {
-            if (mCurrView != null && !mLongPressSent) {
+            if (mTouchedView != null && !mLongPressSent) {
                 mLongPressSent = true;
-                if (mCurrView instanceof ExpandableNotificationRow) {
-                    mCurrView.getLocationOnScreen(mViewOffset);
+                if (mTouchedView instanceof ExpandableNotificationRow) {
+                    mTouchedView.getLocationOnScreen(mViewOffset);
                     final int x = (int) mDownLocation[0] - mViewOffset[0];
                     final int y = (int) mDownLocation[1] - mViewOffset[1];
-                    mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                    ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y);
+                    mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                    ((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
                 }
             }
         }
@@ -281,10 +282,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(final MotionEvent ev) {
-        if (mCurrView instanceof ExpandableNotificationRow) {
-            NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mCurrView).getProvider();
+        if (mTouchedView instanceof ExpandableNotificationRow) {
+            NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mTouchedView).getProvider();
             if (nmr != null) {
-                mMenuRowIntercepting = nmr.onInterceptTouchEvent(mCurrView, ev);
+                mMenuRowIntercepting = nmr.onInterceptTouchEvent(mTouchedView, ev);
             }
         }
         final int action = ev.getAction();
@@ -292,19 +293,19 @@
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mTouchAboveFalsingThreshold = false;
-                mDragging = false;
+                mIsSwiping = false;
                 mSnappingChild = false;
                 mLongPressSent = false;
                 mVelocityTracker.clear();
-                mCurrView = mCallback.getChildAtPosition(ev);
+                mTouchedView = mCallback.getChildAtPosition(ev);
 
-                if (mCurrView != null) {
-                    onDownUpdate(mCurrView, ev);
-                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
+                if (mTouchedView != null) {
+                    onDownUpdate(mTouchedView, ev);
+                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mTouchedView);
                     mVelocityTracker.addMovement(ev);
                     mInitialTouchPos = getPos(ev);
                     mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
-                    mTranslation = getTranslation(mCurrView);
+                    mTranslation = getTranslation(mTouchedView);
                     mDownLocation[0] = ev.getRawX();
                     mDownLocation[1] = ev.getRawY();
                     mHandler.postDelayed(mPerformLongPress, mLongPressTimeout);
@@ -312,7 +313,7 @@
                 break;
 
             case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null && !mLongPressSent) {
+                if (mTouchedView != null && !mLongPressSent) {
                     mVelocityTracker.addMovement(ev);
                     float pos = getPos(ev);
                     float perpendicularPos = getPerpendicularPos(ev);
@@ -325,11 +326,11 @@
                             : mPagingTouchSlop;
                     if (Math.abs(delta) > pagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
-                        if (mCallback.canChildBeDragged(mCurrView)) {
-                            mCallback.onBeginDrag(mCurrView);
-                            mDragging = true;
+                        if (mCallback.canChildBeDragged(mTouchedView)) {
+                            mCallback.onBeginDrag(mTouchedView);
+                            mIsSwiping = true;
                             mInitialTouchPos = getPos(ev);
-                            mTranslation = getTranslation(mCurrView);
+                            mTranslation = getTranslation(mTouchedView);
                         }
                         cancelLongPress();
                     } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS
@@ -343,16 +344,16 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                final boolean captured = (mDragging || mLongPressSent || mMenuRowIntercepting);
-                mDragging = false;
-                mCurrView = null;
+                final boolean captured = (mIsSwiping || mLongPressSent || mMenuRowIntercepting);
+                mIsSwiping = false;
+                mTouchedView = null;
                 mLongPressSent = false;
                 mMenuRowIntercepting = false;
                 cancelLongPress();
                 if (captured) return true;
                 break;
         }
-        return mDragging || mLongPressSent || mMenuRowIntercepting;
+        return mIsSwiping || mLongPressSent || mMenuRowIntercepting;
     }
 
     /**
@@ -451,6 +452,7 @@
                 }
                 if (!mCancelled || wasRemoved) {
                     mCallback.onChildDismissed(animView);
+                    resetSwipeState();
                 }
                 if (endAction != null) {
                     endAction.run();
@@ -501,6 +503,7 @@
                     updateSwipeProgressFromOffset(animView, canBeDismissed);
                     onChildSnappedBack(animView, targetLeft);
                     mCallback.onChildSnappedBack(animView, targetLeft);
+                    resetSwipeState();
                 }
             }
         });
@@ -563,7 +566,7 @@
      * @param targetLeft the target to snap to.
      */
     public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) {
-        if ((mDragging && mCurrView == view) || mSnappingChild) {
+        if ((mIsSwiping && mTouchedView == view) || mSnappingChild) {
             return;
         }
         boolean needToSnap = false;
@@ -589,7 +592,7 @@
             return true;
         }
 
-        if (!mDragging && !mMenuRowIntercepting) {
+        if (!mIsSwiping && !mMenuRowIntercepting) {
             if (mCallback.getChildAtPosition(ev) != null) {
 
                 // We are dragging directly over a card, make sure that we also catch the gesture
@@ -610,7 +613,7 @@
         switch (action) {
             case MotionEvent.ACTION_OUTSIDE:
             case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null) {
+                if (mTouchedView != null) {
                     float delta = getPos(ev) - mInitialTouchPos;
                     float absDelta = Math.abs(delta);
                     if (absDelta >= getFalsingThreshold()) {
@@ -618,9 +621,10 @@
                     }
                     // don't let items that can't be dismissed be dragged more than
                     // maxScrollDistance
-                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(mCurrView,
+                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
+                            mTouchedView,
                             delta > 0)) {
-                        float size = getSize(mCurrView);
+                        float size = getSize(mTouchedView);
                         float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
                         if (absDelta >= size) {
                             delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
@@ -636,32 +640,30 @@
                         }
                     }
 
-                    setTranslation(mCurrView, mTranslation + delta);
-                    updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
-                    onMoveUpdate(mCurrView, ev, mTranslation + delta, delta);
+                    setTranslation(mTouchedView, mTranslation + delta);
+                    updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
+                    onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                if (mCurrView == null) {
+                if (mTouchedView == null) {
                     break;
                 }
                 mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
                 float velocity = getVelocity(mVelocityTracker);
 
-                if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
+                if (!handleUpEvent(ev, mTouchedView, velocity, getTranslation(mTouchedView))) {
                     if (isDismissGesture(ev)) {
-                        // flingadingy
-                        dismissChild(mCurrView, velocity,
+                        dismissChild(mTouchedView, velocity,
                                 !swipedFastEnough() /* useAccelerateInterpolator */);
                     } else {
-                        // snappity
-                        mCallback.onDragCancelled(mCurrView);
-                        snapChild(mCurrView, 0 /* leftTarget */, velocity);
+                        mCallback.onDragCancelled(mTouchedView);
+                        snapChild(mTouchedView, 0 /* leftTarget */, velocity);
                     }
-                    mCurrView = null;
+                    mTouchedView = null;
                 }
-                mDragging = false;
+                mIsSwiping = false;
                 break;
         }
         return true;
@@ -689,17 +691,18 @@
     }
 
     protected boolean swipedFarEnough() {
-        float translation = getTranslation(mCurrView);
+        float translation = getTranslation(mTouchedView);
         return DISMISS_IF_SWIPED_FAR_ENOUGH
-                && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(mCurrView);
+                && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
+                mTouchedView);
     }
 
     public boolean isDismissGesture(MotionEvent ev) {
-        float translation = getTranslation(mCurrView);
+        float translation = getTranslation(mTouchedView);
         return ev.getActionMasked() == MotionEvent.ACTION_UP
                 && !mFalsingManager.isUnlockingDisabled()
                 && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
-                && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
+                && mCallback.canChildBeDismissedInDirection(mTouchedView, translation > 0);
     }
 
     /** Returns true if the gesture should be rejected. */
@@ -715,7 +718,7 @@
 
     protected boolean swipedFastEnough() {
         float velocity = getVelocity(mVelocityTracker);
-        float translation = getTranslation(mCurrView);
+        float translation = getTranslation(mTouchedView);
         boolean ret = (Math.abs(velocity) > getEscapeVelocity())
                 && (velocity > 0) == (translation > 0);
         return ret;
@@ -726,6 +729,20 @@
         return false;
     }
 
+    public boolean isSwiping() {
+        return mIsSwiping;
+    }
+
+    @Nullable
+    public View getSwipedView() {
+        return mIsSwiping ? mTouchedView : null;
+    }
+
+    public void resetSwipeState() {
+        mTouchedView = null;
+        mIsSwiping = false;
+    }
+
     public interface Callback {
         View getChildAtPosition(MotionEvent ev);
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index bb7906e..714d267bb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -23,6 +23,7 @@
 import android.content.ContentProvider;
 import android.content.Context;
 import android.content.Intent;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -94,7 +95,7 @@
                         } catch (NoSuchMethodException
                                 | IllegalAccessException
                                 | InvocationTargetException e) {
-                            // no-op
+                            Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e);
                         }
                     }
             );
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 2b33f8c..182b3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -101,7 +101,7 @@
 
     @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle;
 
-    private @ContainerState int mContainerState = STATE_UNKNOWN;
+    @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
 
     // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
     @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
@@ -632,10 +632,11 @@
         mWindowManager.removeView(this);
     }
 
-    private void onDialogAnimatedIn() {
+    @VisibleForTesting
+    void onDialogAnimatedIn() {
         if (mContainerState == STATE_PENDING_DISMISS) {
             Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
-            animateAway(false /* sendReason */, 0);
+            animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
             return;
         }
         mContainerState = STATE_SHOWING;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3fc9f4d..7bb8a84 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -187,12 +187,14 @@
                 // TODO(b/152419866): Use the UDFPS window type when it becomes available.
                 WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                 PixelFormat.TRANSLUCENT);
         mCoreLayoutParams.setTitle(TAG);
         mCoreLayoutParams.setFitInsetsTypes(0);
+        mCoreLayoutParams.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
         mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
         mView.setSensorProperties(mSensorProps);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
deleted file mode 100644
index 4585fe9..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.view.MotionEvent;
-
-import java.util.HashMap;
-
-/**
- * A classifier which looks at the speed and distance between successive points of a Stroke.
- * It looks at two consecutive speeds between two points and calculates the ratio between them.
- * The final result is the maximum of these values. It does the same for distances. If some speed
- * or distance is equal to zero then the ratio between this and the next part is not calculated. To
- * the duration of each part there is added one nanosecond so that it is always possible to
- * calculate the speed of a part.
- */
-public class AccelerationClassifier extends StrokeClassifier {
-    private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
-
-    public AccelerationClassifier(ClassifierData classifierData) {
-        mClassifierData = classifierData;
-    }
-
-    @Override
-    public String getTag() {
-        return "ACC";
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mStrokeMap.clear();
-        }
-
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
-            Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
-            if (mStrokeMap.get(stroke) == null) {
-                mStrokeMap.put(stroke, new Data(point));
-            } else {
-                mStrokeMap.get(stroke).addPoint(point);
-            }
-        }
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        Data data = mStrokeMap.get(stroke);
-        return 2 * SpeedRatioEvaluator.evaluate(data.maxSpeedRatio);
-    }
-
-    private static class Data {
-
-        static final float MILLIS_TO_NANOS = 1e6f;
-
-        Point previousPoint;
-        float previousSpeed = 0;
-        float maxSpeedRatio = 0;
-
-        public Data(Point point) {
-            previousPoint = point;
-        }
-
-        public void addPoint(Point point) {
-            float distance = previousPoint.dist(point);
-            float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
-            float speed = distance / duration;
-
-            if (duration > 20 * MILLIS_TO_NANOS || duration < 5 * MILLIS_TO_NANOS) {
-                // reject this segment and ensure we won't use data about it in the next round.
-                previousSpeed = 0;
-                previousPoint = point;
-                return;
-            }
-            if (previousSpeed != 0.0f) {
-                maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
-            }
-
-            previousSpeed = speed;
-            previousPoint = point;
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
deleted file mode 100644
index 6d13973..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.os.Build;
-import android.os.SystemProperties;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * A classifier which calculates the variance of differences between successive angles in a stroke.
- * For each stroke it keeps its last three points. If some successive points are the same, it
- * ignores the repetitions. If a new point is added, the classifier calculates the angle between
- * the last three points. After that, it calculates the difference between this angle and the
- * previously calculated angle. Then it calculates the variance of the differences from a stroke.
- * To the differences there is artificially added value 0.0 and the difference between the first
- * angle and PI (angles are in radians). It helps with strokes which have few points and punishes
- * more strokes which are not smooth.
- *
- * This classifier also tries to split the stroke into two parts in the place in which the biggest
- * angle is. It calculates the angle variance of the two parts and sums them up. The reason the
- * classifier is doing this, is because some human swipes at the beginning go for a moment in one
- * direction and then they rapidly change direction for the rest of the stroke (like a tick). The
- * final result is the minimum of angle variance of the whole stroke and the sum of angle variances
- * of the two parts split up. The classifier tries the tick option only if the first part is
- * shorter than the second part.
- *
- * Additionally, the classifier classifies the angles as left angles (those angles which value is
- * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles
- * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles
- * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are
- * in the same direction (straight angles can be left angels or right angles)
- */
-public class AnglesClassifier extends StrokeClassifier {
-    private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
-
-    public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.ang",
-            Build.IS_DEBUGGABLE);
-
-    private static String TAG = "ANG";
-
-    public AnglesClassifier(ClassifierData classifierData) {
-        mClassifierData = classifierData;
-    }
-
-    @Override
-    public String getTag() {
-        return TAG;
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mStrokeMap.clear();
-        }
-
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
-
-            if (mStrokeMap.get(stroke) == null) {
-                mStrokeMap.put(stroke, new Data());
-            }
-            mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
-        }
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        Data data = mStrokeMap.get(stroke);
-        return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance(), type)
-                + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage(), type);
-    }
-
-    private static class Data {
-        private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
-
-        private List<Point> mLastThreePoints = new ArrayList<>();
-        private float mFirstAngleVariance;
-        private float mPreviousAngle;
-        private float mBiggestAngle;
-        private float mSumSquares;
-        private float mSecondSumSquares;
-        private float mSum;
-        private float mSecondSum;
-        private float mCount;
-        private float mSecondCount;
-        private float mFirstLength;
-        private float mLength;
-        private float mAnglesCount;
-        private float mLeftAngles;
-        private float mRightAngles;
-        private float mStraightAngles;
-
-        public Data() {
-            mFirstAngleVariance = 0.0f;
-            mPreviousAngle = (float) Math.PI;
-            mBiggestAngle = 0.0f;
-            mSumSquares = mSecondSumSquares = 0.0f;
-            mSum = mSecondSum = 0.0f;
-            mCount = mSecondCount = 1.0f;
-            mLength = mFirstLength = 0.0f;
-            mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
-        }
-
-        public void addPoint(Point point) {
-            // Checking if the added point is different than the previously added point
-            // Repetitions are being ignored so that proper angles are calculated.
-            if (mLastThreePoints.isEmpty()
-                    || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
-                if (!mLastThreePoints.isEmpty()) {
-                    mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
-                }
-                mLastThreePoints.add(point);
-                if (mLastThreePoints.size() == 4) {
-                    mLastThreePoints.remove(0);
-
-                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
-                            mLastThreePoints.get(2));
-
-                    mAnglesCount++;
-                    if (angle < Math.PI - ANGLE_DEVIATION) {
-                        mLeftAngles++;
-                    } else if (angle <= Math.PI + ANGLE_DEVIATION) {
-                        mStraightAngles++;
-                    } else {
-                        mRightAngles++;
-                    }
-
-                    float difference = angle - mPreviousAngle;
-
-                    // If this is the biggest angle of the stroke so then we save the value of
-                    // the angle variance so far and start to count the values for the angle
-                    // variance of the second part.
-                    if (mBiggestAngle < angle) {
-                        mBiggestAngle = angle;
-                        mFirstLength = mLength;
-                        mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
-                        mSecondSumSquares = 0.0f;
-                        mSecondSum = 0.0f;
-                        mSecondCount = 1.0f;
-                    } else {
-                        mSecondSum += difference;
-                        mSecondSumSquares += difference * difference;
-                        mSecondCount += 1.0;
-                    }
-
-                    mSum += difference;
-                    mSumSquares += difference * difference;
-                    mCount += 1.0;
-                    mPreviousAngle = angle;
-                }
-            }
-        }
-
-        public float getAnglesVariance(float sumSquares, float sum, float count) {
-            return sumSquares / count - (sum / count) * (sum / count);
-        }
-
-        public float getAnglesVariance() {
-            float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
-            if (VERBOSE) {
-                FalsingLog.i(TAG, "getAnglesVariance: (first pass) " + anglesVariance);
-                FalsingLog.i(TAG, "   - mFirstLength=" + mFirstLength);
-                FalsingLog.i(TAG, "   - mLength=" + mLength);
-            }
-            if (mFirstLength < mLength / 2f) {
-                anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
-                        + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
-                if (VERBOSE) FalsingLog.i(TAG, "getAnglesVariance: (second pass) " + anglesVariance);
-            }
-            return anglesVariance;
-        }
-
-        public float getAnglesPercentage() {
-            if (mAnglesCount == 0.0f) {
-                if (VERBOSE) FalsingLog.i(TAG, "getAnglesPercentage: count==0, result=1");
-                return 1.0f;
-            }
-            final float result = (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
-            if (VERBOSE) {
-                FalsingLog.i(TAG, "getAnglesPercentage: left=" + mLeftAngles + " right="
-                        + mRightAngles + " straight=" + mStraightAngles + " count=" + mAnglesCount
-                        + " result=" + result);
-            }
-            return result;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
deleted file mode 100644
index e6e42f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class AnglesPercentageEvaluator {
-    public static float evaluate(float value, int type) {
-        final boolean secureUnlock = type == Classifier.BOUNCER_UNLOCK;
-        float evaluation = 0.0f;
-        if (value < 1.00 && !secureUnlock) evaluation++;
-        if (value < 0.90 && !secureUnlock) evaluation++;
-        if (value < 0.70) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
deleted file mode 100644
index 9ffe783..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class AnglesVarianceEvaluator {
-    public static float evaluate(float value, int type) {
-        float evaluation = 0.0f;
-        if (value > 0.20) evaluation++;
-        if (value > 0.40) evaluation++;
-        if (value > 0.80) evaluation++;
-        if (value > 1.50) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index ae7d142..c7ad3e8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -54,11 +54,6 @@
     public @interface InteractionType {}
 
     /**
-     * Contains all the information about touch events from which the classifier can query
-     */
-    protected ClassifierData mClassifierData;
-
-    /**
      * Informs the classifier that a new touch event has occurred
      */
     public void onTouchEvent(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
deleted file mode 100644
index 587abba..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.util.SparseArray;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-/**
- * Contains data which is used to classify interaction sequences on the lockscreen. It does, for
- * example, provide information on the current touch state.
- */
-public class ClassifierData {
-    private static final long MINIMUM_DT_NANOS = 16666666;  // 60Hz
-    private static final long MINIMUM_DT_SMEAR_NANOS = 2500000;  // 2.5ms
-
-    private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
-    private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
-    private final float mDpi;
-
-    public ClassifierData(float dpi) {
-        mDpi = dpi;
-    }
-
-    /** Returns true if the event should be considered, false otherwise. */
-    public boolean update(MotionEvent event) {
-        // We limit to 60hz sampling. Drop anything happening faster than that.
-        // Legacy code was created with an assumed sampling rate. As devices increase their
-        // sampling rate, this creates potentialy false positives.
-        if (event.getActionMasked() == MotionEvent.ACTION_MOVE
-                && mCurrentStrokes.size() != 0
-                && event.getEventTimeNano() - mCurrentStrokes.valueAt(0).getLastEventTimeNano()
-                        < MINIMUM_DT_NANOS - MINIMUM_DT_SMEAR_NANOS) {
-            return false;
-        }
-
-        mEndingStrokes.clear();
-        int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            mCurrentStrokes.clear();
-        }
-
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            int id = event.getPointerId(i);
-            if (mCurrentStrokes.get(id) == null) {
-                mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi));
-            }
-            mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
-                    event.getEventTimeNano());
-
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
-                    || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
-                mEndingStrokes.add(getStroke(id));
-            }
-        }
-
-        return true;
-    }
-
-    public void cleanUp(MotionEvent event) {
-        mEndingStrokes.clear();
-        int action = event.getActionMasked();
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            int id = event.getPointerId(i);
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
-                    || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
-                mCurrentStrokes.remove(id);
-            }
-        }
-    }
-
-    /**
-     * @return the list of Strokes which are ending in the recently added MotionEvent
-     */
-    public ArrayList<Stroke> getEndingStrokes() {
-        return mEndingStrokes;
-    }
-
-    /**
-     * @param id the id from MotionEvent
-     * @return the Stroke assigned to the id
-     */
-    public Stroke getStroke(int id) {
-        return mCurrentStrokes.get(id);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
deleted file mode 100644
index 610e219..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the general direction of a stroke and evaluates it depending on
- * the type of action that takes place.
- */
-public class DirectionClassifier extends StrokeClassifier {
-    public DirectionClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "DIR";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        Point firstPoint = stroke.getPoints().get(0);
-        Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
-        return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y,
-                type);
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
deleted file mode 100644
index 78b4168..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class DirectionEvaluator {
-    public static float evaluate(float xDiff, float yDiff, int type) {
-        float falsingEvaluation = 5.5f;
-        boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff);
-        switch (type) {
-            case Classifier.QUICK_SETTINGS:
-            case Classifier.PULSE_EXPAND:
-            case Classifier.NOTIFICATION_DRAG_DOWN:
-                if (!vertical || yDiff <= 0.0) {
-                    return falsingEvaluation;
-                }
-                break;
-            case Classifier.NOTIFICATION_DISMISS:
-                if (vertical) {
-                    return falsingEvaluation;
-                }
-                break;
-            case Classifier.UNLOCK:
-            case Classifier.BOUNCER_UNLOCK:
-                if (!vertical || yDiff >= 0.0) {
-                    return falsingEvaluation;
-                }
-                break;
-            case Classifier.LEFT_AFFORDANCE:
-                if (xDiff < 0.0 && yDiff > 0.0) {
-                    return falsingEvaluation;
-                }
-                break;
-            case Classifier.RIGHT_AFFORDANCE:
-                if (xDiff > 0.0 && yDiff > 0.0) {
-                    return falsingEvaluation;
-                }
-            default:
-                break;
-        }
-        return 0.0f;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
deleted file mode 100644
index 77fda20..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the ratio between the duration of the stroke and its number of
- * points.
- */
-public class DurationCountClassifier extends StrokeClassifier {
-    public DurationCountClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "DUR";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
deleted file mode 100644
index 5395983..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-
-public class DurationCountEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 0.0105) evaluation++;
-        if (value < 0.00909) evaluation++;
-        if (value < 0.00667) evaluation++;
-        if (value > 0.0333) evaluation++;
-        if (value > 0.0500) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
deleted file mode 100644
index de8a188..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the distance between the first and the last point from the stroke.
- */
-public class EndPointLengthClassifier extends StrokeClassifier {
-    public EndPointLengthClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "END_LNGTH";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
deleted file mode 100644
index bb2f1c4..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class EndPointLengthEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 0.05) evaluation += 2.0;
-        if (value < 0.1) evaluation += 2.0;
-        if (value < 0.2) evaluation += 2.0;
-        if (value < 0.3) evaluation += 2.0;
-        if (value < 0.4) evaluation += 2.0;
-        if (value < 0.5) evaluation += 2.0;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
deleted file mode 100644
index 9b6ddc8..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the ratio between the total length covered by the stroke and the
- * distance between the first and last point from this stroke.
- */
-public class EndPointRatioClassifier extends StrokeClassifier {
-    public EndPointRatioClassifier(ClassifierData classifierData) {
-        mClassifierData = classifierData;
-    }
-
-    @Override
-    public String getTag() {
-        return "END_RTIO";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        float ratio;
-        if (stroke.getTotalLength() == 0.0f) {
-            ratio = 1.0f;
-        } else {
-            ratio = stroke.getEndPointLength() / stroke.getTotalLength();
-        }
-        return EndPointRatioEvaluator.evaluate(ratio);
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
deleted file mode 100644
index 529fcec..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class EndPointRatioEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 0.85) evaluation++;
-        if (value < 0.75) evaluation++;
-        if (value < 0.65) evaluation++;
-        if (value < 0.55) evaluation++;
-        if (value < 0.45) evaluation++;
-        if (value < 0.35) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
deleted file mode 100644
index 8105c6a..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2016 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.classifier;
-
-import android.app.ActivityThread;
-import android.app.Application;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.Date;
-import java.util.Locale;
-
-/**
- * Keeps track of interesting falsing data.
- *
- * By default the log only gets collected on userdebug builds. To turn it on on user:
- *  adb shell setprop debug.falsing_log true
- *
- * The log gets dumped as part of the SystemUI services. To dump on demand:
- *  adb shell dumpsys activity service com.android.systemui StatusBar | grep -A 999 FALSING | less
- *
- * To dump into logcat:
- *  adb shell setprop debug.falsing_logcat true
- *
- * To adjust the log buffer size:
- *  adb shell setprop debug.falsing_log_size 200
- */
-public class FalsingLog {
-    public static final boolean ENABLED = SystemProperties.getBoolean("debug.falsing_log",
-            Build.IS_DEBUGGABLE);
-    private static final boolean LOGCAT = SystemProperties.getBoolean("debug.falsing_logcat",
-            false);
-
-    public static final boolean VERBOSE = false;
-
-    private static final int MAX_SIZE = SystemProperties.getInt("debug.falsing_log_size", 100);
-
-    private static final String TAG = "FalsingLog";
-
-    private final ArrayDeque<String> mLog = new ArrayDeque<>(MAX_SIZE);
-    private final SimpleDateFormat mFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
-
-    private static FalsingLog sInstance;
-
-    private FalsingLog() {
-    }
-
-    public static void v(String tag, String s) {
-        if (!VERBOSE) {
-            return;
-        }
-        if (LOGCAT) {
-            Log.v(TAG, tag + "\t" + s);
-        }
-        log("V", tag, s);
-    }
-
-    public static void i(String tag, String s) {
-        if (LOGCAT) {
-            Log.i(TAG, tag + "\t" + s);
-        }
-        log("I", tag, s);
-    }
-
-    public static void wLogcat(String tag, String s) {
-        Log.w(TAG, tag + "\t" + s);
-        log("W", tag, s);
-    }
-
-    public static void w(String tag, String s) {
-        if (LOGCAT) {
-            Log.w(TAG, tag + "\t" + s);
-        }
-        log("W", tag, s);
-    }
-
-    public static void e(String tag, String s) {
-        if (LOGCAT) {
-            Log.e(TAG, tag + "\t" + s);
-        }
-        log("E", tag, s);
-    }
-
-    public static synchronized void log(String level, String tag, String s) {
-        if (!ENABLED) {
-            return;
-        }
-        if (sInstance == null) {
-            sInstance = new FalsingLog();
-        }
-
-        if (sInstance.mLog.size() >= MAX_SIZE) {
-            sInstance.mLog.removeFirst();
-        }
-        String entry = new StringBuilder().append(sInstance.mFormat.format(new Date()))
-            .append(" ").append(level).append(" ")
-            .append(tag).append(" ").append(s).toString();
-        sInstance.mLog.add(entry);
-    }
-
-    public static synchronized void dump(PrintWriter pw) {
-        pw.println("FALSING LOG:");
-        if (!ENABLED) {
-            pw.println("Disabled, to enable: setprop debug.falsing_log 1");
-            pw.println();
-            return;
-        }
-        if (sInstance == null || sInstance.mLog.isEmpty()) {
-            pw.println("<empty>");
-            pw.println();
-            return;
-        }
-        for (String s : sInstance.mLog) {
-            pw.println(s);
-        }
-        pw.println();
-    }
-
-    public static synchronized void wtf(String tag, String s, Throwable here) {
-        if (!ENABLED) {
-            return;
-        }
-        e(tag, s);
-
-        Application application = ActivityThread.currentApplication();
-        String fileMessage = "";
-        if (Build.IS_DEBUGGABLE && application != null) {
-            File f = new File(application.getDataDir(), "falsing-"
-                    + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt");
-            PrintWriter pw = null;
-            try {
-                pw = new PrintWriter(f);
-                dump(pw);
-                pw.close();
-                fileMessage = "Log written to " + f.getAbsolutePath();
-            } catch (IOException e) {
-                Log.e(TAG, "Unable to write falsing log", e);
-            } finally {
-                if (pw != null) {
-                    pw.close();
-                }
-            }
-        } else {
-            Log.e(TAG, "Unable to write log, build must be debuggable.");
-        }
-
-        Log.wtf(TAG, tag + " " + s + "; " + fileMessage, here);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 6961b45..1214843 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -22,6 +22,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.plugins.FalsingManager;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -29,10 +30,13 @@
  */
 public class FalsingManagerFake implements FalsingManager {
     private boolean mIsFalseTouch;
+    private boolean mIsFalseTap;
+    private boolean mIsFalseDoubleTap;
     private boolean mIsUnlockingDisabled;
     private boolean mIsClassiferEnabled;
     private boolean mShouldEnforceBouncer;
     private boolean mIsReportingEnabled;
+    private boolean mIsFalseRobustTap;
 
     @Override
     public void onSuccessfulUnlock() {
@@ -74,6 +78,28 @@
         return mIsFalseTouch;
     }
 
+    public void setFalseRobustTap(boolean falseRobustTap) {
+        mIsFalseRobustTap = falseRobustTap;
+    }
+
+    public void setFalseTap(boolean falseTap) {
+        mIsFalseTap = falseTap;
+    }
+
+    public void setFalseDoubleTap(boolean falseDoubleTap) {
+        mIsFalseDoubleTap = falseDoubleTap;
+    }
+
+    @Override
+    public boolean isFalseTap(boolean robustCheck) {
+        return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
+    }
+
+    @Override
+    public boolean isFalseDoubleTap() {
+        return mIsFalseDoubleTap;
+    }
+
     @Override
     public void onNotificatonStopDraggingDown() {
 
@@ -236,8 +262,7 @@
     }
 
     @Override
-    public void dump(PrintWriter pw) {
-
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
deleted file mode 100644
index decaec1..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ /dev/null
@@ -1,579 +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.classifier;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.hardware.biometrics.BiometricSourceType;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
-import com.android.systemui.analytics.DataCollector;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.sensors.AsyncSensorManager;
-
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-
-/**
- * When the phone is locked, listens to touch, sensor and phone events and sends them to
- * DataCollector and HumanInteractionClassifier.
- *
- * It does not collect touch events when the bouncer shows up.
- */
-public class FalsingManagerImpl implements FalsingManager {
-    private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
-
-    private static final int[] CLASSIFIER_SENSORS = new int[] {
-            Sensor.TYPE_PROXIMITY,
-    };
-
-    private static final int[] COLLECTOR_SENSORS = new int[] {
-            Sensor.TYPE_ACCELEROMETER,
-            Sensor.TYPE_GYROSCOPE,
-            Sensor.TYPE_PROXIMITY,
-            Sensor.TYPE_LIGHT,
-            Sensor.TYPE_ROTATION_VECTOR,
-    };
-    public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
-    public static final String FALSING_SUCCESS = "falsing_success_after_attempts";
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final Context mContext;
-
-    private final SensorManager mSensorManager;
-    private final DataCollector mDataCollector;
-    private final HumanInteractionClassifier mHumanInteractionClassifier;
-    private final AccessibilityManager mAccessibilityManager;
-    private final Executor mUiBgExecutor;
-
-    private boolean mEnforceBouncer = false;
-    private boolean mBouncerOn = false;
-    private boolean mBouncerOffOnDown = false;
-    private boolean mSessionActive = false;
-    private boolean mIsTouchScreen = true;
-    private boolean mJustUnlockedWithFace = false;
-    private int mState = StatusBarState.SHADE;
-    private boolean mScreenOn;
-    private boolean mShowingAod;
-    private Runnable mPendingWtf;
-    private int mIsFalseTouchCalls;
-    private MetricsLogger mMetricsLogger;
-
-    private SensorEventListener mSensorEventListener = new SensorEventListener() {
-        @Override
-        public synchronized void onSensorChanged(SensorEvent event) {
-            mDataCollector.onSensorChanged(event);
-            mHumanInteractionClassifier.onSensorChanged(event);
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            mDataCollector.onAccuracyChanged(sensor, accuracy);
-        }
-    };
-
-    public StateListener mStatusBarStateListener = new StateListener() {
-        @Override
-        public void onStateChanged(int newState) {
-            if (FalsingLog.ENABLED) {
-                FalsingLog.i("setStatusBarState", new StringBuilder()
-                        .append("from=").append(StatusBarState.toShortString(mState))
-                        .append(" to=").append(StatusBarState.toShortString(newState))
-                        .toString());
-            }
-            mState = newState;
-            updateSessionActive();
-        }
-    };
-
-    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateConfiguration();
-        }
-    };
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onBiometricAuthenticated(int userId,
-                        BiometricSourceType biometricSourceType,
-                        boolean isStrongBiometric) {
-                    if (userId == KeyguardUpdateMonitor.getCurrentUser()
-                            && biometricSourceType == BiometricSourceType.FACE) {
-                        mJustUnlockedWithFace = true;
-                    }
-                }
-            };
-
-    FalsingManagerImpl(Context context, @UiBackground Executor uiBgExecutor) {
-        mContext = context;
-        mSensorManager = Dependency.get(AsyncSensorManager.class);
-        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
-        mDataCollector = DataCollector.getInstance(mContext);
-        mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
-        mUiBgExecutor = uiBgExecutor;
-        mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
-        mMetricsLogger = new MetricsLogger();
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        updateConfiguration();
-        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
-        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mKeyguardUpdateCallback);
-    }
-
-    private void updateConfiguration() {
-        mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
-                ENFORCE_BOUNCER, 0);
-    }
-
-    private boolean shouldSessionBeActive() {
-        if (FalsingLog.ENABLED && FalsingLog.VERBOSE) {
-            FalsingLog.v("shouldBeActive", new StringBuilder()
-                    .append("enabled=").append(isEnabled() ? 1 : 0)
-                    .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
-                    .append(" mState=").append(StatusBarState.toShortString(mState))
-                    .append(" mShowingAod=").append(mShowingAod ? 1 : 0)
-                    .toString()
-            );
-        }
-        return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
-    }
-
-    private boolean sessionEntrypoint() {
-        if (!mSessionActive && shouldSessionBeActive()) {
-            onSessionStart();
-            return true;
-        }
-        return false;
-    }
-
-    private void sessionExitpoint(boolean force) {
-        if (mSessionActive && (force || !shouldSessionBeActive())) {
-            mSessionActive = false;
-            if (mIsFalseTouchCalls != 0) {
-                if (FalsingLog.ENABLED) {
-                    FalsingLog.i(
-                            "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls);
-                }
-                mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls);
-                mIsFalseTouchCalls = 0;
-            }
-
-            // This can be expensive, and doesn't need to happen on the main thread.
-            mUiBgExecutor.execute(() -> {
-                mSensorManager.unregisterListener(mSensorEventListener);
-            });
-        }
-    }
-
-    public void updateSessionActive() {
-        if (shouldSessionBeActive()) {
-            sessionEntrypoint();
-        } else {
-            sessionExitpoint(false /* force */);
-        }
-    }
-
-    private void onSessionStart() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassifierEnabled());
-            clearPendingWtf();
-        }
-        mBouncerOn = false;
-        mSessionActive = true;
-        mJustUnlockedWithFace = false;
-        mIsFalseTouchCalls = 0;
-
-        if (mHumanInteractionClassifier.isEnabled()) {
-            registerSensors(CLASSIFIER_SENSORS);
-        }
-        if (mDataCollector.isEnabledFull()) {
-            registerSensors(COLLECTOR_SENSORS);
-        }
-        if (mDataCollector.isEnabled()) {
-            mDataCollector.onFalsingSessionStarted();
-        }
-    }
-
-    private void registerSensors(int [] sensors) {
-        for (int sensorType : sensors) {
-            Sensor s = mSensorManager.getDefaultSensor(sensorType);
-            if (s != null) {
-
-                // This can be expensive, and doesn't need to happen on the main thread.
-                mUiBgExecutor.execute(() -> {
-                    mSensorManager.registerListener(
-                            mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
-                });
-            }
-        }
-    }
-
-    public boolean isClassifierEnabled() {
-        return mHumanInteractionClassifier.isEnabled();
-    }
-
-    private boolean isEnabled() {
-        return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
-    }
-
-    public boolean isUnlockingDisabled() {
-        return mDataCollector.isUnlockingDisabled();
-    }
-    /**
-     * @return true if the classifier determined that this is not a human interacting with the phone
-     */
-    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
-        if (FalsingLog.ENABLED) {
-            // We're getting some false wtfs from touches that happen after the device went
-            // to sleep. Only report missing sessions that happen when the device is interactive.
-            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()
-                    && mPendingWtf == null) {
-                int enabled = isEnabled() ? 1 : 0;
-                int screenOn = mScreenOn ? 1 : 0;
-                String state = StatusBarState.toShortString(mState);
-                Throwable here = new Throwable("here");
-                FalsingLog.wLogcat("isFalseTouch", new StringBuilder()
-                        .append("Session is not active, yet there's a query for a false touch.")
-                        .append(" enabled=").append(enabled)
-                        .append(" mScreenOn=").append(screenOn)
-                        .append(" mState=").append(state)
-                        .append(". Escalating to WTF if screen does not turn on soon.")
-                        .toString());
-
-                // Unfortunately we're also getting false positives for touches that happen right
-                // after the screen turns on, but before that notification has made it to us.
-                // Unfortunately there's no good way to catch that, except to wait and see if we get
-                // the screen on notification soon.
-                mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder()
-                        .append("Session did not become active after query for a false touch.")
-                        .append(" enabled=").append(enabled)
-                        .append('/').append(isEnabled() ? 1 : 0)
-                        .append(" mScreenOn=").append(screenOn)
-                        .append('/').append(mScreenOn ? 1 : 0)
-                        .append(" mState=").append(state)
-                        .append('/').append(StatusBarState.toShortString(mState))
-                        .append(". Look for warnings ~1000ms earlier to see root cause.")
-                        .toString(), here);
-                mHandler.postDelayed(mPendingWtf, 1000);
-            }
-        }
-        if (ActivityManager.isRunningInUserTestHarness()) {
-            // This is a test device running UiAutomator code.
-            return false;
-        }
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            // Touch exploration triggers false positives in the classifier and
-            // already sufficiently prevents false unlocks.
-            return false;
-        }
-        if (!mIsTouchScreen) {
-            // Unlocking with input devices besides the touchscreen should already be sufficiently
-            // anti-falsed.
-            return false;
-        }
-        if (mJustUnlockedWithFace) {
-            // Unlocking with face is a strong user presence signal, we can assume the user
-            // is present until the next session starts.
-            return false;
-        }
-        mIsFalseTouchCalls++;
-        boolean isFalse = mHumanInteractionClassifier.isFalseTouch();
-        if (!isFalse) {
-            if (FalsingLog.ENABLED) {
-                FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls);
-            }
-            mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
-            mIsFalseTouchCalls = 0;
-        }
-        return isFalse;
-    }
-
-    private void clearPendingWtf() {
-        if (mPendingWtf != null) {
-            mHandler.removeCallbacks(mPendingWtf);
-            mPendingWtf = null;
-        }
-    }
-
-
-    public boolean shouldEnforceBouncer() {
-        return mEnforceBouncer;
-    }
-
-    public void setShowingAod(boolean showingAod) {
-        mShowingAod = showingAod;
-        updateSessionActive();
-    }
-
-    public void onScreenTurningOn() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onScreenTurningOn", new StringBuilder()
-                    .append("from=").append(mScreenOn ? 1 : 0)
-                    .toString());
-            clearPendingWtf();
-        }
-        mScreenOn = true;
-        if (sessionEntrypoint()) {
-            mDataCollector.onScreenTurningOn();
-        }
-    }
-
-    public void onScreenOnFromTouch() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
-                    .append("from=").append(mScreenOn ? 1 : 0)
-                    .toString());
-        }
-        mScreenOn = true;
-        if (sessionEntrypoint()) {
-            mDataCollector.onScreenOnFromTouch();
-        }
-    }
-
-    public void onScreenOff() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onScreenOff", new StringBuilder()
-                    .append("from=").append(mScreenOn ? 1 : 0)
-                    .toString());
-        }
-        mDataCollector.onScreenOff();
-        mScreenOn = false;
-        sessionExitpoint(false /* force */);
-    }
-
-    public void onSuccessfulUnlock() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onSucccessfulUnlock", "");
-        }
-        mDataCollector.onSucccessfulUnlock();
-    }
-
-    public void onBouncerShown() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onBouncerShown", new StringBuilder()
-                    .append("from=").append(mBouncerOn ? 1 : 0)
-                    .toString());
-        }
-        if (!mBouncerOn) {
-            mBouncerOn = true;
-            mDataCollector.onBouncerShown();
-        }
-    }
-
-    public void onBouncerHidden() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onBouncerHidden", new StringBuilder()
-                    .append("from=").append(mBouncerOn ? 1 : 0)
-                    .toString());
-        }
-        if (mBouncerOn) {
-            mBouncerOn = false;
-            mDataCollector.onBouncerHidden();
-        }
-    }
-
-    public void onQsDown() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onQsDown", "");
-        }
-        mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
-        mDataCollector.onQsDown();
-    }
-
-    public void setQsExpanded(boolean expanded) {
-        mDataCollector.setQsExpanded(expanded);
-    }
-
-    public void onTrackingStarted(boolean secure) {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onTrackingStarted", "");
-        }
-        mHumanInteractionClassifier.setType(secure
-                ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK);
-        mDataCollector.onTrackingStarted();
-    }
-
-    public void onTrackingStopped() {
-        mDataCollector.onTrackingStopped();
-    }
-
-    public void onNotificationActive() {
-        mDataCollector.onNotificationActive();
-    }
-
-    public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onNotificationDoubleTap", "accepted=" + accepted
-                    + " dx=" + dx + " dy=" + dy + " (px)");
-        }
-        mDataCollector.onNotificationDoubleTap();
-    }
-
-    public void setNotificationExpanded() {
-        mDataCollector.setNotificationExpanded();
-    }
-
-    public void onNotificatonStartDraggingDown() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onNotificatonStartDraggingDown", "");
-        }
-        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
-        mDataCollector.onNotificatonStartDraggingDown();
-    }
-
-    public void onStartExpandingFromPulse() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onStartExpandingFromPulse", "");
-        }
-        mHumanInteractionClassifier.setType(Classifier.PULSE_EXPAND);
-        mDataCollector.onStartExpandingFromPulse();
-    }
-
-    public void onNotificatonStopDraggingDown() {
-        mDataCollector.onNotificatonStopDraggingDown();
-    }
-
-    public void onExpansionFromPulseStopped() {
-        mDataCollector.onExpansionFromPulseStopped();
-    }
-
-    public void onNotificationDismissed() {
-        mDataCollector.onNotificationDismissed();
-    }
-
-    public void onNotificationStartDismissing() {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onNotificationStartDismissing", "");
-        }
-        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
-        mDataCollector.onNotificatonStartDismissing();
-    }
-
-    public void onNotificationStopDismissing() {
-        mDataCollector.onNotificatonStopDismissing();
-    }
-
-    public void onCameraOn() {
-        mDataCollector.onCameraOn();
-    }
-
-    public void onLeftAffordanceOn() {
-        mDataCollector.onLeftAffordanceOn();
-    }
-
-    public void onAffordanceSwipingStarted(boolean rightCorner) {
-        if (FalsingLog.ENABLED) {
-            FalsingLog.i("onAffordanceSwipingStarted", "");
-        }
-        if (rightCorner) {
-            mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
-        } else {
-            mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
-        }
-        mDataCollector.onAffordanceSwipingStarted(rightCorner);
-    }
-
-    public void onAffordanceSwipingAborted() {
-        mDataCollector.onAffordanceSwipingAborted();
-    }
-
-    public void onUnlockHintStarted() {
-        mDataCollector.onUnlockHintStarted();
-    }
-
-    public void onCameraHintStarted() {
-        mDataCollector.onCameraHintStarted();
-    }
-
-    public void onLeftAffordanceHintStarted() {
-        mDataCollector.onLeftAffordanceHintStarted();
-    }
-
-    public void onTouchEvent(MotionEvent event, int width, int height) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
-            // If the bouncer was not shown during the down event,
-            // we want the entire gesture going to HumanInteractionClassifier
-            mBouncerOffOnDown = !mBouncerOn;
-        }
-        if (mSessionActive) {
-            if (!mBouncerOn) {
-                // In case bouncer is "visible", but onFullyShown has not yet been called,
-                // avoid adding the event to DataCollector
-                mDataCollector.onTouchEvent(event, width, height);
-            }
-            if (mBouncerOffOnDown) {
-                mHumanInteractionClassifier.onTouchEvent(event);
-            }
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("FALSING MANAGER");
-        pw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
-        pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
-        pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
-        pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
-        pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
-        pw.println();
-    }
-
-    @Override
-    public void cleanup() {
-        mSensorManager.unregisterListener(mSensorEventListener);
-        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
-        Dependency.get(StatusBarStateController.class).removeCallback(mStatusBarStateListener);
-        Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mKeyguardUpdateCallback);
-    }
-
-    public Uri reportRejectedTouch() {
-        if (mDataCollector.isEnabled()) {
-            return mDataCollector.reportRejectedTouch();
-        }
-        return null;
-    }
-
-    public boolean isReportingEnabled() {
-        return mDataCollector.isReportingEnabled();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 2c31862..814fff9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -16,13 +16,13 @@
 
 package com.android.systemui.classifier;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
-
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.SensorManager;
 import android.net.Uri;
 import android.provider.DeviceConfig;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 import androidx.annotation.NonNull;
 
@@ -33,7 +33,6 @@
 import com.android.systemui.classifier.brightline.FalsingDataProvider;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.FalsingManager;
@@ -53,52 +52,63 @@
 /**
  * Simple passthrough implementation of {@link FalsingManager} allowing plugins to swap in.
  *
- * {@link FalsingManagerImpl} is used when a Plugin is not loaded.
+ * {@link BrightLineFalsingManager} is used when a Plugin is not loaded.
  */
 @SysUISingleton
 public class FalsingManagerProxy implements FalsingManager, Dumpable {
 
     private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
+    private static final String DUMPABLE_TAG = "FalsingManager";
+    public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
+    public static final String FALSING_SUCCESS = "falsing_success_after_attempts";
 
+    private final PluginManager mPluginManager;
     private final ProximitySensor mProximitySensor;
+    private final Resources mResources;
+    private final ViewConfiguration mViewConfiguration;
     private final FalsingDataProvider mFalsingDataProvider;
-    private FalsingManager mInternalFalsingManager;
-    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
     private final DeviceConfigProxy mDeviceConfig;
-    private boolean mBrightlineEnabled;
     private final DockManager mDockManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private Executor mUiBgExecutor;
+    private final DumpManager mDumpManager;
     private final StatusBarStateController mStatusBarStateController;
+    final PluginListener<FalsingPlugin> mPluginListener;
+
+    private FalsingManager mInternalFalsingManager;
+
+    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
+            properties -> onDeviceConfigPropertiesChanged(properties.getNamespace());
 
     @Inject
-    FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
+    FalsingManagerProxy(PluginManager pluginManager, @Main Executor executor,
             ProximitySensor proximitySensor,
             DeviceConfigProxy deviceConfig, DockManager dockManager,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             DumpManager dumpManager,
-            @UiBackground Executor uiBgExecutor,
             StatusBarStateController statusBarStateController,
+            @Main Resources resources,
+            ViewConfiguration viewConfiguration,
             FalsingDataProvider falsingDataProvider) {
+        mPluginManager = pluginManager;
         mProximitySensor = proximitySensor;
         mDockManager = dockManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mUiBgExecutor = uiBgExecutor;
+        mDumpManager = dumpManager;
         mStatusBarStateController = statusBarStateController;
+        mResources = resources;
+        mViewConfiguration = viewConfiguration;
         mFalsingDataProvider = falsingDataProvider;
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
         mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
         mDeviceConfig = deviceConfig;
-        mDeviceConfigListener =
-                properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
-        setupFalsingManager(context);
+        setupFalsingManager();
         mDeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 executor,
                 mDeviceConfigListener
         );
 
-        final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() {
+        mPluginListener = new PluginListener<FalsingPlugin>() {
             public void onPluginConnected(FalsingPlugin plugin, Context context) {
                 FalsingManager pluginFalsingManager = plugin.getFalsingManager(context);
                 if (pluginFalsingManager != null) {
@@ -108,49 +118,42 @@
             }
 
             public void onPluginDisconnected(FalsingPlugin plugin) {
-                mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
+                setupFalsingManager();
             }
         };
 
-        pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
+        mPluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
 
-        dumpManager.registerDumpable("FalsingManager", this);
+        mDumpManager.registerDumpable(DUMPABLE_TAG, this);
     }
 
-    private void onDeviceConfigPropertiesChanged(Context context, String namespace) {
+    private void onDeviceConfigPropertiesChanged(String namespace) {
         if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
             return;
         }
 
-        setupFalsingManager(context);
+        setupFalsingManager();
     }
 
     /**
-     * Chooses the FalsingManager implementation.
+     * Setup the FalsingManager implementation.
+     *
+     * If multiple implementations are available, this is where the choice is made.
      */
-    private void setupFalsingManager(Context context) {
-        boolean brightlineEnabled = mDeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true);
-        if (brightlineEnabled == mBrightlineEnabled && mInternalFalsingManager != null) {
-            return;
-        }
-        mBrightlineEnabled = brightlineEnabled;
-
+    private void setupFalsingManager() {
         if (mInternalFalsingManager != null) {
             mInternalFalsingManager.cleanup();
         }
-        if (!brightlineEnabled) {
-            mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
-        } else {
-            mInternalFalsingManager = new BrightLineFalsingManager(
-                    mFalsingDataProvider,
-                    mKeyguardUpdateMonitor,
-                    mProximitySensor,
-                    mDeviceConfig,
-                    mDockManager,
-                    mStatusBarStateController
-            );
-        }
+        mInternalFalsingManager = new BrightLineFalsingManager(
+                mFalsingDataProvider,
+                mKeyguardUpdateMonitor,
+                mProximitySensor,
+                mDeviceConfig,
+                mResources,
+                mViewConfiguration,
+                mDockManager,
+                mStatusBarStateController
+        );
     }
 
     /**
@@ -191,6 +194,17 @@
         return mInternalFalsingManager.isFalseTouch(interactionType);
     }
 
+
+    @Override
+    public boolean isFalseTap(boolean robustCheck) {
+        return mInternalFalsingManager.isFalseTap(robustCheck);
+    }
+
+    @Override
+    public boolean isFalseDoubleTap() {
+        return mInternalFalsingManager.isFalseDoubleTap();
+    }
+
     @Override
     public void onNotificatonStopDraggingDown() {
         mInternalFalsingManager.onNotificatonStartDraggingDown();
@@ -338,17 +352,14 @@
 
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
-        mInternalFalsingManager.dump(pw);
-    }
-
-    @Override
-    public void dump(PrintWriter pw) {
-        mInternalFalsingManager.dump(pw);
+        mInternalFalsingManager.dump(fd, pw, args);
     }
 
     @Override
     public void cleanup() {
         mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
+        mPluginManager.removePluginListener(mPluginListener);
+        mDumpManager.unregisterDumpable(DUMPABLE_TAG);
         mInternalFalsingManager.cleanup();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
deleted file mode 100644
index 11388fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * An abstract class for classifiers which classify the whole gesture (all the strokes which
- * occurred from DOWN event to UP/CANCEL event)
- */
-public abstract class GestureClassifier extends Classifier {
-
-    /**
-     * @param type the type of action for which this method is called
-     * @return a non-negative value which is used to determine whether the most recent gesture is a
-     *         false interaction; the bigger the value the greater the chance that this a false
-     *         interaction.
-     */
-    public abstract float getFalseTouchEvaluation(int type);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
deleted file mode 100644
index 4c64711..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.os.SystemClock;
-
-import java.util.ArrayList;
-
-/**
- * Holds the evaluations for ended strokes and gestures. These values are decreased through time.
- */
-public class HistoryEvaluator {
-    private static final float INTERVAL = 50.0f;
-    private static final float HISTORY_FACTOR = 0.9f;
-    private static final float EPSILON = 1e-5f;
-
-    private final ArrayList<Data> mStrokes = new ArrayList<>();
-    private final ArrayList<Data> mGestureWeights = new ArrayList<>();
-    private long mLastUpdate;
-
-    public HistoryEvaluator() {
-        mLastUpdate = SystemClock.elapsedRealtime();
-    }
-
-    public void addStroke(float evaluation) {
-        decayValue();
-        mStrokes.add(new Data(evaluation));
-    }
-
-    public void addGesture(float evaluation) {
-        decayValue();
-        mGestureWeights.add(new Data(evaluation));
-    }
-
-    /**
-     * Calculates the weighted average of strokes and adds to it the weighted average of gestures
-     */
-    public float getEvaluation() {
-        return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
-    }
-
-    private float weightedAverage(ArrayList<Data> list) {
-        float sumValue = 0.0f;
-        float sumWeight = 0.0f;
-        int size = list.size();
-        for (int i = 0; i < size; i++) {
-            Data data = list.get(i);
-            sumValue += data.evaluation * data.weight;
-            sumWeight += data.weight;
-        }
-
-        if (sumWeight == 0.0f) {
-            return 0.0f;
-        }
-
-        return sumValue / sumWeight;
-    }
-
-    private void decayValue() {
-        long time = SystemClock.elapsedRealtime();
-
-        if (time <= mLastUpdate) {
-            return;
-        }
-
-        // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
-        float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL);
-
-        decayValue(mStrokes, factor);
-        decayValue(mGestureWeights, factor);
-        mLastUpdate = time;
-    }
-
-    private void decayValue(ArrayList<Data> list, float factor) {
-        int size = list.size();
-        for (int i = 0; i < size; i++) {
-            list.get(i).weight *= factor;
-        }
-
-        // Removing evaluations with such small weights that they do not matter anymore
-        while (!list.isEmpty() && isZero(list.get(0).weight)) {
-            list.remove(0);
-        }
-    }
-
-    private boolean isZero(float x) {
-        return x <= EPSILON && x >= -EPSILON;
-    }
-
-    /**
-     * For each stroke it holds its initial value and the current weight. Initially the
-     * weight is set to 1.0
-     */
-    private static class Data {
-        public float evaluation;
-        public float weight;
-
-        public Data(float evaluation) {
-            this.evaluation = evaluation;
-            weight = 1.0f;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
deleted file mode 100644
index 86dccb2..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.SensorEvent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-
-import com.android.systemui.R;
-
-import java.util.ArrayDeque;
-
-/**
- * An classifier trying to determine whether it is a human interacting with the phone or not.
- */
-public class HumanInteractionClassifier extends Classifier {
-    private static final String HIC_ENABLE = "HIC_enable";
-    private static final float FINGER_DISTANCE = 0.1f;
-
-    private static HumanInteractionClassifier sInstance = null;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final Context mContext;
-
-    private final StrokeClassifier[] mStrokeClassifiers;
-    private final GestureClassifier[] mGestureClassifiers;
-    private final ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
-    private final HistoryEvaluator mHistoryEvaluator;
-    private final float mDpi;
-
-    private boolean mEnableClassifier = false;
-    private int mCurrentType = Classifier.GENERIC;
-
-    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateConfiguration();
-        }
-    };
-
-    private HumanInteractionClassifier(Context context) {
-        mContext = context;
-        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-
-        // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
-        // were to be used separately. Due negligible differences in xdpi and ydpi we can just
-        // take the average.
-        // Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling.
-        mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
-        mClassifierData = new ClassifierData(mDpi);
-        mHistoryEvaluator = new HistoryEvaluator();
-
-        mStrokeClassifiers = new StrokeClassifier[]{
-                new AnglesClassifier(mClassifierData),
-                new SpeedClassifier(mClassifierData),
-                new DurationCountClassifier(mClassifierData),
-                new EndPointRatioClassifier(mClassifierData),
-                new EndPointLengthClassifier(mClassifierData),
-                new AccelerationClassifier(mClassifierData),
-                new SpeedAnglesClassifier(mClassifierData),
-                new LengthCountClassifier(mClassifierData),
-                new DirectionClassifier(mClassifierData),
-        };
-
-        mGestureClassifiers = new GestureClassifier[] {
-                new PointerCountClassifier(mClassifierData),
-                new ProximityClassifier(mClassifierData)
-        };
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(HIC_ENABLE), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        updateConfiguration();
-    }
-
-    public static HumanInteractionClassifier getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new HumanInteractionClassifier(context);
-        }
-        return sInstance;
-    }
-
-    private void updateConfiguration() {
-        boolean defaultValue = mContext.getResources().getBoolean(
-                R.bool.config_lockscreenAntiFalsingClassifierEnabled);
-
-        mEnableClassifier = 0 != Settings.Global.getInt(
-                mContext.getContentResolver(),
-                HIC_ENABLE, defaultValue ? 1 : 0);
-    }
-
-    public void setType(int type) {
-        mCurrentType = type;
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        if (!mEnableClassifier) {
-            return;
-        }
-
-        // If the user is dragging down the notification, they might want to drag it down
-        // enough to see the content, read it for a while and then lift the finger to open
-        // the notification. This kind of motion scores very bad in the Classifier so the
-        // MotionEvents which are close to the current position of the finger are not
-        // sent to the classifiers until the finger moves far enough. When the finger if lifted
-        // up, the last MotionEvent which was far enough from the finger is set as the final
-        // MotionEvent and sent to the Classifiers.
-        if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN
-                || mCurrentType == Classifier.PULSE_EXPAND) {
-            mBufferedEvents.add(MotionEvent.obtain(event));
-            Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi);
-
-            while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi,
-                    mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) {
-                addTouchEvent(mBufferedEvents.getFirst());
-                mBufferedEvents.remove();
-            }
-
-            int action = event.getActionMasked();
-            if (action == MotionEvent.ACTION_UP) {
-                mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP);
-                addTouchEvent(mBufferedEvents.getFirst());
-                mBufferedEvents.clear();
-            }
-        } else {
-            addTouchEvent(event);
-        }
-    }
-
-    private void addTouchEvent(MotionEvent event) {
-        if (!mClassifierData.update(event)) {
-            return;
-        }
-
-        for (StrokeClassifier c : mStrokeClassifiers) {
-            c.onTouchEvent(event);
-        }
-
-        for (GestureClassifier c : mGestureClassifiers) {
-            c.onTouchEvent(event);
-        }
-
-        int size = mClassifierData.getEndingStrokes().size();
-        for (int i = 0; i < size; i++) {
-            Stroke stroke = mClassifierData.getEndingStrokes().get(i);
-            float evaluation = 0.0f;
-            StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("stroke") : null;
-            for (StrokeClassifier c : mStrokeClassifiers) {
-                float e = c.getFalseTouchEvaluation(mCurrentType, stroke);
-                if (FalsingLog.ENABLED) {
-                    String tag = c.getTag();
-                    sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
-                }
-                evaluation += e;
-            }
-
-            if (FalsingLog.ENABLED) {
-                FalsingLog.i(" addTouchEvent", sb.toString());
-            }
-            mHistoryEvaluator.addStroke(evaluation);
-        }
-
-        int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            float evaluation = 0.0f;
-            StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("gesture") : null;
-            for (GestureClassifier c : mGestureClassifiers) {
-                float e = c.getFalseTouchEvaluation(mCurrentType);
-                if (FalsingLog.ENABLED) {
-                    String tag = c.getTag();
-                    sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
-                }
-                evaluation += e;
-            }
-            if (FalsingLog.ENABLED) {
-                FalsingLog.i(" addTouchEvent", sb.toString());
-            }
-            mHistoryEvaluator.addGesture(evaluation);
-            setType(Classifier.GENERIC);
-        }
-
-        mClassifierData.cleanUp(event);
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        for (Classifier c : mStrokeClassifiers) {
-            c.onSensorChanged(event);
-        }
-
-        for (Classifier c : mGestureClassifiers) {
-            c.onSensorChanged(event);
-        }
-    }
-
-    public boolean isFalseTouch() {
-        if (mEnableClassifier) {
-            float evaluation = mHistoryEvaluator.getEvaluation();
-            boolean result = evaluation >= 5.0f;
-            if (FalsingLog.ENABLED) {
-                FalsingLog.i("isFalseTouch", new StringBuilder()
-                        .append("eval=").append(evaluation).append(" result=")
-                        .append(result ? 1 : 0).toString());
-            }
-            return result;
-        }
-        return false;
-    }
-
-    public boolean isEnabled() {
-        return mEnableClassifier;
-    }
-
-    @Override
-    public String getTag() {
-        return "HIC";
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
deleted file mode 100644
index 53678a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the ratio between the length of the stroke and its number of
- * points. The number of points is subtracted by 2 because the UP event comes in with some delay
- * and it should not influence the ratio and also strokes which are long and have a small number
- * of points are punished more (these kind of strokes are usually bad ones and they tend to score
- * well in other classifiers).
- */
-public class LengthCountClassifier extends StrokeClassifier {
-    public LengthCountClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "LEN_CNT";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return LengthCountEvaluator.evaluate(stroke.getTotalLength()
-                / Math.max(1.0f, stroke.getCount() - 2));
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
deleted file mode 100644
index dac7a6f..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier which looks at the ratio between the length of the stroke and its number of
- * points.
- */
-public class LengthCountEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 0.09) evaluation++;
-        if (value < 0.05) evaluation++;
-        if (value < 0.02) evaluation++;
-        if (value > 0.6) evaluation++;
-        if (value > 0.9) evaluation++;
-        if (value > 1.2) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Point.java b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
deleted file mode 100644
index f3dc2be..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/Point.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class Point {
-    public float x;
-    public float y;
-    public long timeOffsetNano;
-
-    public Point(float x, float y) {
-        this.x = x;
-        this.y = y;
-        this.timeOffsetNano = 0;
-    }
-
-    public Point(float x, float y, long timeOffsetNano) {
-        this.x = x;
-        this.y = y;
-        this.timeOffsetNano = timeOffsetNano;
-    }
-
-    public boolean equals(Point p) {
-        return x == p.x && y == p.y;
-    }
-
-    public float dist(Point a) {
-        return (float) Math.hypot(a.x - x, a.y - y);
-    }
-
-    /**
-     * Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the
-     * vector from point x to point y
-     */
-    public float crossProduct(Point a, Point b) {
-        return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x);
-    }
-
-    /**
-     * Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the
-     * vector from point x to point y
-     */
-    public float dotProduct(Point a, Point b) {
-        return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
-    }
-
-    /**
-     * Calculates the angle in radians created by points (a, this, b). If any two of these points
-     * are the same, the method will return 0.0f
-     *
-     * @return the angle in radians
-     */
-    public float getAngle(Point a, Point b) {
-        float dist1 = dist(a);
-        float dist2 = dist(b);
-
-        if (dist1 == 0.0f || dist2 == 0.0f) {
-            return 0.0f;
-        }
-
-        float crossProduct = crossProduct(a, b);
-        float dotProduct = dotProduct(a, b);
-        float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
-        float angle = (float) Math.acos(cos);
-        if (crossProduct < 0.0) {
-            angle = 2.0f * (float) Math.PI - angle;
-        }
-        return angle;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
deleted file mode 100644
index 136c433..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.view.MotionEvent;
-
-/**
- * A classifier which looks at the total number of traces in the whole gesture.
- */
-public class PointerCountClassifier extends GestureClassifier {
-    private int mCount;
-
-    public PointerCountClassifier(ClassifierData classifierData) {
-        mCount = 0;
-    }
-
-    @Override
-    public String getTag() {
-        return "PTR_CNT";
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mCount = 1;
-        }
-
-        if (action == MotionEvent.ACTION_POINTER_DOWN) {
-            ++mCount;
-        }
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type) {
-        return PointerCountEvaluator.evaluate(mCount);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
deleted file mode 100644
index 62adfc8..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.view.MotionEvent;
-
-/**
- * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
- * the proximity sensor showing the near state during the whole gesture
- */
-public class ProximityClassifier extends GestureClassifier {
-    private long mGestureStartTimeNano;
-    private long mNearStartTimeNano;
-    private long mNearDuration;
-    private boolean mNear;
-    private float mAverageNear;
-
-    public ProximityClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "PROX";
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
-            update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
-        }
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mGestureStartTimeNano = event.getEventTimeNano();
-            mNearStartTimeNano = event.getEventTimeNano();
-            mNearDuration = 0;
-        }
-
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            update(mNear, event.getEventTimeNano());
-            long duration = event.getEventTimeNano() - mGestureStartTimeNano;
-
-            if (duration == 0) {
-                mAverageNear = mNear ? 1.0f : 0.0f;
-            } else {
-                mAverageNear = (float) mNearDuration / (float) duration;
-            }
-        }
-    }
-
-
-    /**
-     * @param near is the sensor showing the near state right now
-     * @param timestampNano time of this event in nanoseconds
-     */
-    private void update(boolean near, long timestampNano) {
-        // This if is necessary because MotionEvents and SensorEvents do not come in
-        // chronological order
-        if (timestampNano > mNearStartTimeNano) {
-            // if the state before was near then add the difference of the current time and
-            // mNearStartTimeNano to mNearDuration.
-            if (mNear) {
-                mNearDuration += timestampNano - mNearStartTimeNano;
-            }
-
-            // if the new state is near, set mNearStartTimeNano equal to this moment.
-            if (near) {
-                mNearStartTimeNano = timestampNano;
-            }
-        }
-        mNear = near;
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type) {
-        return ProximityEvaluator.evaluate(mAverageNear, type);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
deleted file mode 100644
index 91002bf..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class ProximityEvaluator {
-    public static float evaluate(float value, int type) {
-        float evaluation = 0.0f;
-        float threshold = 0.1f;
-        if (type == Classifier.QUICK_SETTINGS) {
-            threshold = 1.0f;
-        }
-        if (value >= threshold) evaluation += 2.0;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
deleted file mode 100644
index 66f0cf6..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import android.os.Build;
-import android.os.SystemProperties;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * A classifier which for each point from a stroke, it creates a point on plane with coordinates
- * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
- * and then it calculates the angle variance of these points like the class
- * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores
- * the last point of a stroke because the UP event comes in with some delay and this ruins the
- * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
- * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
- * does that is because the speed of a good stroke is most often increases, so most of these angels
- * should be in this interval.
- */
-public class SpeedAnglesClassifier extends StrokeClassifier {
-    public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.spd_ang",
-            Build.IS_DEBUGGABLE);
-    public static final String TAG = "SPD_ANG";
-
-    private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
-
-    public SpeedAnglesClassifier(ClassifierData classifierData) {
-        mClassifierData = classifierData;
-    }
-
-    @Override
-    public String getTag() {
-        return TAG;
-    }
-
-    @Override
-    public void onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mStrokeMap.clear();
-        }
-
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
-
-            if (mStrokeMap.get(stroke) == null) {
-                mStrokeMap.put(stroke, new Data());
-            }
-
-            if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
-                    && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
-                mStrokeMap.get(stroke).addPoint(
-                        stroke.getPoints().get(stroke.getPoints().size() - 1));
-            }
-        }
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        Data data = mStrokeMap.get(stroke);
-        return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
-                + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
-    }
-
-    private static class Data {
-        private final float DURATION_SCALE = 1e8f;
-        private final float LENGTH_SCALE = 1.0f;
-        private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
-
-        private List<Point> mLastThreePoints = new ArrayList<>();
-        private Point mPreviousPoint;
-        private float mPreviousAngle;
-        private float mSumSquares;
-        private float mSum;
-        private float mCount;
-        private float mDist;
-        private float mAnglesCount;
-        private float mAcceleratingAngles;
-
-        public Data() {
-            mPreviousPoint = null;
-            mPreviousAngle = (float) Math.PI;
-            mSumSquares = 0.0f;
-            mSum = 0.0f;
-            mCount = 1.0f;
-            mDist = 0.0f;
-            mAnglesCount = mAcceleratingAngles = 0.0f;
-        }
-
-        public void addPoint(Point point) {
-            if (mPreviousPoint != null) {
-                mDist += mPreviousPoint.dist(point);
-            }
-
-            mPreviousPoint = point;
-            Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
-                    mDist / LENGTH_SCALE);
-
-            // Checking if the added point is different than the previously added point
-            // Repetitions are being ignored so that proper angles are calculated.
-            if (mLastThreePoints.isEmpty()
-                    || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
-                mLastThreePoints.add(speedPoint);
-                if (mLastThreePoints.size() == 4) {
-                    mLastThreePoints.remove(0);
-
-                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
-                            mLastThreePoints.get(2));
-
-                    mAnglesCount++;
-                    if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
-                        mAcceleratingAngles++;
-                    }
-
-                    float difference = angle - mPreviousAngle;
-                    mSum += difference;
-                    mSumSquares += difference * difference;
-                    mCount += 1.0;
-                    mPreviousAngle = angle;
-                }
-            }
-        }
-
-        public float getAnglesVariance() {
-            final float v = mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
-            if (VERBOSE) {
-                FalsingLog.i(TAG, "getAnglesVariance: sum^2=" + mSumSquares
-                        + " count=" + mCount + " result=" + v);
-            }
-            return v;
-        }
-
-        public float getAnglesPercentage() {
-            if (mAnglesCount == 0.0f) {
-                return 1.0f;
-            }
-            final float v = (mAcceleratingAngles) / mAnglesCount;
-            if (VERBOSE) {
-                FalsingLog.i(TAG, "getAnglesPercentage: angles=" + mAcceleratingAngles
-                        + " count=" + mAnglesCount + " result=" + v);
-            }
-            return v;
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
deleted file mode 100644
index d50d406..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class SpeedAnglesPercentageEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 1.00) evaluation++;
-        if (value < 0.90) evaluation++;
-        if (value < 0.70) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
deleted file mode 100644
index 01fcc37..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in
- * inches per second.
- */
-public class SpeedClassifier extends StrokeClassifier {
-    private final float NANOS_TO_SECONDS = 1e9f;
-
-    public SpeedClassifier(ClassifierData classifierData) {
-    }
-
-    @Override
-    public String getTag() {
-        return "SPD";
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
-        if (duration == 0.0f) {
-            return SpeedEvaluator.evaluate(0.0f);
-        }
-        return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
deleted file mode 100644
index afd8d01..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class SpeedEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value < 4.0) evaluation++;
-        if (value < 2.2) evaluation++;
-        if (value > 35.0) evaluation++;
-        if (value > 50.0) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
deleted file mode 100644
index e34f222..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class SpeedRatioEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value == 0) return 0;
-        if (value <= 1.0) evaluation++;
-        if (value <= 0.5) evaluation++;
-        if (value > 9.0) evaluation++;
-        if (value > 18.0) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
deleted file mode 100644
index 48b1b6e..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-public class SpeedVarianceEvaluator {
-    public static float evaluate(float value) {
-        float evaluation = 0.0f;
-        if (value > 0.06) evaluation++;
-        if (value > 0.15) evaluation++;
-        if (value > 0.3) evaluation++;
-        if (value > 0.6) evaluation++;
-        return evaluation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
deleted file mode 100644
index 977a2d0..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-import java.util.ArrayList;
-
-/**
- * Contains data about a stroke (a single trace, all the events from a given id from the
- * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
- */
-public class Stroke {
-    private final float NANOS_TO_SECONDS = 1e9f;
-
-    private ArrayList<Point> mPoints = new ArrayList<>();
-    private long mStartTimeNano;
-    private long mEndTimeNano;
-    private float mLength;
-    private final float mDpi;
-
-    public Stroke(long eventTimeNano, float dpi) {
-        mDpi = dpi;
-        mStartTimeNano = mEndTimeNano = eventTimeNano;
-    }
-
-    public void addPoint(float x, float y, long eventTimeNano) {
-        mEndTimeNano = eventTimeNano;
-        Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
-        if (!mPoints.isEmpty()) {
-            mLength += mPoints.get(mPoints.size() - 1).dist(point);
-        }
-        mPoints.add(point);
-    }
-
-    public int getCount() {
-        return mPoints.size();
-    }
-
-    public float getTotalLength() {
-        return mLength;
-    }
-
-    public float getEndPointLength() {
-        return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
-    }
-
-    public long getDurationNanos() {
-        return mEndTimeNano - mStartTimeNano;
-    }
-
-    public float getDurationSeconds() {
-        return (float) getDurationNanos() / NANOS_TO_SECONDS;
-    }
-
-    public ArrayList<Point> getPoints() {
-        return mPoints;
-    }
-
-    public long getLastEventTimeNano() {
-        if (mPoints.isEmpty()) {
-            return mStartTimeNano;
-        }
-
-        return mPoints.get(mPoints.size() - 1).timeOffsetNano;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
deleted file mode 100644
index 5da392f..0000000
--- a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 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.classifier;
-
-/**
- * An abstract class for classifiers which classify each stroke separately.
- */
-public abstract class StrokeClassifier extends Classifier {
-
-    /**
-     * @param type the type of action for which this method is called
-     * @param stroke the stroke for which the evaluation will be calculated
-     * @return a non-negative value which is used to determine whether this a false touch; the
-     *         bigger the value the greater the chance that this a false touch
-     */
-    public abstract float getFalseTouchEvaluation(int type, Stroke stroke);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 9d847ca..334102d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -16,29 +16,37 @@
 
 package com.android.systemui.classifier.brightline;
 
-import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED;
-import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS;
+import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_REMAIN_LOCKED;
+import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
 
 import android.app.ActivityManager;
+import android.content.res.Resources;
 import android.hardware.biometrics.BiometricSourceType;
 import android.net.Uri;
 import android.os.Build;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.NonNull;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
 import com.android.systemui.classifier.Classifier;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.NotificationTapHelper;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -64,6 +72,8 @@
     private final ProximitySensor mProximitySensor;
     private final DockManager mDockManager;
     private final StatusBarStateController mStatusBarStateController;
+    private final SingleTapClassifier mSingleTapClassifier;
+    private final DoubleTapClassifier mDoubleTapClassifier;
     private boolean mSessionStarted;
     private MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
@@ -106,8 +116,9 @@
 
     public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
             KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
-            DeviceConfigProxy deviceConfigProxy,
-            DockManager dockManager, StatusBarStateController statusBarStateController) {
+            DeviceConfigProxy deviceConfigProxy, @Main Resources resources,
+            ViewConfiguration viewConfiguration, DockManager dockManager,
+            StatusBarStateController statusBarStateController) {
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDataProvider = falsingDataProvider;
         mProximitySensor = proximitySensor;
@@ -129,6 +140,12 @@
         mClassifiers.add(distanceClassifier);
         mClassifiers.add(proximityClassifier);
         mClassifiers.add(new ZigZagClassifier(mDataProvider, deviceConfigProxy));
+
+        mSingleTapClassifier = new SingleTapClassifier(
+                mDataProvider, viewConfiguration.getScaledTouchSlop());
+        mDoubleTapClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier,
+                resources.getDimension(R.dimen.double_tap_slop),
+                NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS);
     }
 
     private void registerSensors() {
@@ -237,6 +254,36 @@
     }
 
     @Override
+    public boolean isFalseTap(boolean robustCheck) {
+        if (!mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents())) {
+            logInfo(String.format(
+                    (Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName()));
+            String reason = mSingleTapClassifier.getReason();
+            if (reason != null) {
+                logInfo(reason);
+            }
+            return true;
+        }
+
+        // TODO(b/172655679): we always reject single-taps when doing a robust check for now.
+        return robustCheck;
+    }
+
+    @Override
+    public boolean isFalseDoubleTap() {
+        boolean result = mDoubleTapClassifier.isFalseTouch();
+        if (result) {
+            logInfo(String.format(
+                    (Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName()));
+            String reason = mDoubleTapClassifier.getReason();
+            if (reason != null) {
+                logInfo(reason);
+            }
+        }
+        return result;
+    }
+
+    @Override
     public void onTouchEvent(MotionEvent motionEvent, int width, int height) {
         // TODO: some of these classifiers might allow us to abort early, meaning we don't have to
         // make these calls.
@@ -413,7 +460,7 @@
     }
 
     @Override
-    public void dump(PrintWriter pw) {
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         ipw.println("BRIGHTLINE FALSING MANAGER");
         ipw.print("classifierEnabled=");
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java
new file mode 100644
index 0000000..d3af1c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java
@@ -0,0 +1,106 @@
+/*
+ * 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.classifier.brightline;
+
+import android.view.MotionEvent;
+
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Returns a false touch if the most two recent gestures are not taps or are too far apart.
+ */
+public class DoubleTapClassifier extends FalsingClassifier {
+
+    private final SingleTapClassifier mSingleTapClassifier;
+    private final float mDoubleTapSlop;
+    private final long mDoubleTapTimeMs;
+
+    private StringBuilder mReason = new StringBuilder();
+
+    DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier,
+            float doubleTapSlop, long doubleTapTimeMs) {
+        super(dataProvider);
+        mSingleTapClassifier = singleTapClassifier;
+        mDoubleTapSlop = doubleTapSlop;
+        mDoubleTapTimeMs = doubleTapTimeMs;
+    }
+
+    @Override
+    boolean isFalseTouch() {
+        List<MotionEvent> secondTapEvents = getRecentMotionEvents();
+        Queue<? extends List<MotionEvent>> historicalEvents = getHistoricalEvents();
+        List<MotionEvent> firstTapEvents = historicalEvents.peek();
+
+        mReason = new StringBuilder();
+
+        if (firstTapEvents == null) {
+            mReason.append("Only one gesture recorded");
+            return true;
+        }
+
+        return !isDoubleTap(firstTapEvents, secondTapEvents, mReason);
+    }
+
+    /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */
+    public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents,
+            StringBuilder reason) {
+
+        if (!mSingleTapClassifier.isTap(firstEvents)) {
+            reason.append("First gesture is not a tap. ").append(mSingleTapClassifier.getReason());
+            return false;
+        }
+
+        if (!mSingleTapClassifier.isTap(secondEvents)) {
+            reason.append("Second gesture is not a tap. ").append(mSingleTapClassifier.getReason());
+            return false;
+        }
+
+        MotionEvent firstFinalEvent = firstEvents.get(firstEvents.size() - 1);
+        MotionEvent secondFinalEvent = secondEvents.get(secondEvents.size() - 1);
+
+        long dt = secondFinalEvent.getEventTime() - firstFinalEvent.getEventTime();
+
+        if (dt > mDoubleTapTimeMs) {
+            reason.append("Time between taps too large: ").append(dt).append("ms");
+            return false;
+        }
+
+        if (Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX()) >= mDoubleTapSlop) {
+            reason.append("Delta X between taps too large:")
+                    .append(Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX()))
+                    .append(" vs ")
+                    .append(mDoubleTapSlop);
+            return false;
+        }
+
+        if (Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY()) >= mDoubleTapSlop) {
+            reason.append("Delta Y between taps too large:")
+                    .append(Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY()))
+                    .append(" vs ")
+                    .append(mDoubleTapSlop);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    String getReason() {
+        return mReason.toString();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index 85e95a6..ed417b3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -22,6 +22,7 @@
 import com.android.systemui.util.sensors.ProximitySensor;
 
 import java.util.List;
+import java.util.Queue;
 
 /**
  * Base class for rules that determine False touches.
@@ -30,13 +31,17 @@
     private final FalsingDataProvider mDataProvider;
 
     FalsingClassifier(FalsingDataProvider dataProvider) {
-        this.mDataProvider = dataProvider;
+        mDataProvider = dataProvider;
     }
 
     List<MotionEvent> getRecentMotionEvents() {
         return mDataProvider.getRecentMotionEvents();
     }
 
+    Queue<? extends List<MotionEvent>> getHistoricalEvents() {
+        return mDataProvider.getHistoricalMotionEvents();
+    }
+
     MotionEvent getFirstMotionEvent() {
         return mDataProvider.getFirstRecentMotionEvent();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 8d06748..4681f97 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -23,9 +23,13 @@
 
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.time.SystemClock;
 
 import java.util.ArrayList;
+import java.util.Deque;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Queue;
 
 import javax.inject.Inject;
 
@@ -35,18 +39,21 @@
 public class FalsingDataProvider {
 
     private static final long MOTION_EVENT_AGE_MS = 1000;
+    private static final long EXTENDED_MOTION_EVENT_AGE_MS = 30 * 1000;
     private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
 
     private final int mWidthPixels;
     private final int mHeightPixels;
     private final BatteryController mBatteryController;
+    private final SystemClock mSystemClock;
     private final float mXdpi;
     private final float mYdpi;
 
     private @Classifier.InteractionType int mInteractionType;
-    private final TimeLimitedMotionEventBuffer mRecentMotionEvents =
-            new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
+    private final Deque<TimeLimitedMotionEventBuffer> mExtendedMotionEvents = new LinkedList<>();
 
+    private TimeLimitedMotionEventBuffer mRecentMotionEvents =
+            new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
     private boolean mDirty = true;
 
     private float mAngle = 0;
@@ -55,12 +62,14 @@
     private MotionEvent mLastMotionEvent;
 
     @Inject
-    public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
+    public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController,
+            SystemClock systemClock) {
         mXdpi = displayMetrics.xdpi;
         mYdpi = displayMetrics.ydpi;
         mWidthPixels = displayMetrics.widthPixels;
         mHeightPixels = displayMetrics.heightPixels;
         mBatteryController = batteryController;
+        mSystemClock = systemClock;
 
         FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
         FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -81,7 +90,10 @@
         }
 
         if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mRecentMotionEvents.clear();
+            if (!mRecentMotionEvents.isEmpty()) {
+                mExtendedMotionEvents.addFirst(mRecentMotionEvents);
+                mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
+            }
         }
         mRecentMotionEvents.addAll(motionEvents);
 
@@ -112,6 +124,16 @@
         return mRecentMotionEvents;
     }
 
+    /** Returns recent gestures, exclusive of the most recent gesture. Newer gestures come first. */
+    Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() {
+        long nowMs = mSystemClock.uptimeMillis();
+
+        mExtendedMotionEvents.removeIf(
+                motionEvents -> motionEvents.isFullyExpired(nowMs - EXTENDED_MOTION_EVENT_AGE_MS));
+
+        return mExtendedMotionEvents;
+    }
+
     /**
      * interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java
new file mode 100644
index 0000000..47708f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java
@@ -0,0 +1,68 @@
+/*
+ * 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.classifier.brightline;
+
+import android.view.MotionEvent;
+
+import java.util.List;
+
+/**
+ * Falsing classifier that accepts or rejects a single gesture as a tap.
+ */
+public class SingleTapClassifier extends FalsingClassifier {
+    private final float mTouchSlop;
+    private String mReason;
+
+    SingleTapClassifier(FalsingDataProvider dataProvider, float touchSlop) {
+        super(dataProvider);
+        mTouchSlop = touchSlop;
+    }
+
+    @Override
+    boolean isFalseTouch() {
+        return !isTap(getRecentMotionEvents());
+    }
+
+    /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
+    public boolean isTap(List<MotionEvent> motionEvents) {
+        float downX = motionEvents.get(0).getX();
+        float downY = motionEvents.get(0).getY();
+
+        for (MotionEvent event : motionEvents) {
+            if (Math.abs(event.getX() - downX) >= mTouchSlop) {
+                mReason = "dX too big for a tap: "
+                        + Math.abs(event.getX() - downX)
+                        + "vs "
+                        + mTouchSlop;
+                return false;
+            } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
+                mReason = "dY too big for a tap: "
+                        + Math.abs(event.getY() - downY)
+                        + "vs "
+                        + mTouchSlop;
+                return false;
+            }
+        }
+        mReason = "";
+        return true;
+    }
+
+    @Override
+    String getReason() {
+        return mReason;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java
index 9a83b5b..92aa7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java
@@ -34,12 +34,24 @@
 public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
 
     private final LinkedList<MotionEvent> mMotionEvents;
-    private long mMaxAgeMs;
+    private final long mMaxAgeMs;
 
     TimeLimitedMotionEventBuffer(long maxAgeMs) {
         super();
-        this.mMaxAgeMs = maxAgeMs;
-        this.mMotionEvents = new LinkedList<>();
+        mMaxAgeMs = maxAgeMs;
+        mMotionEvents = new LinkedList<>();
+    }
+
+    /**
+     * Returns true if the most recent event in the buffer is past the expiration time.
+     *
+     * This method does not mutate the underlying data. This method does imply that, if the supplied
+     * expiration time is old enough and a new {@link MotionEvent} gets added to the buffer, all
+     * prior events would be removed.
+     */
+    public boolean isFullyExpired(long expirationMs) {
+        return mMotionEvents.isEmpty()
+                || mMotionEvents.getLast().getEventTime() <= expirationMs;
     }
 
     private void ejectOldEvents() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index d3d24be..96f2072 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -478,7 +478,7 @@
         val pendingIntent = PendingIntent.getActivity(context,
                 componentName.hashCode(),
                 intent,
-                0)
+                PendingIntent.FLAG_IMMUTABLE)
         val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
                 .setTitle(controlInfo.controlTitle)
                 .setSubtitle(controlInfo.controlSubtitle)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 58e49f8..6888d1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -134,7 +134,7 @@
         DOZING_UPDATE_SENSOR_TAP(441),
 
         @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
-        DOZING_UPDATE_AUTH_TRIGGERED(442);
+        DOZING_UPDATE_AUTH_TRIGGERED(657);
 
         private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 37bcb16..bd3b899 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -329,7 +329,7 @@
                     Method injectMethod = rootComponent.getClass()
                             .getMethod("inject", getClass());
                     injectMethod.invoke(rootComponent, this);
-                    Log.w("TAG", "mMediaManager is now: " + mMediaManager);
+                    Log.w(TAG, "mMediaManager is now: " + mMediaManager);
                 } catch (NoSuchMethodException ex) {
                     Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
                 } catch (IllegalAccessException | InvocationTargetException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4c68312..aea0dd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -34,10 +34,12 @@
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricSourceType;
 import android.media.AudioAttributes;
@@ -88,6 +90,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardService;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
@@ -779,7 +782,7 @@
 
         // Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
         // is disabled.
-        if (mContext.getResources().getBoolean(R.bool.config_enableKeyguardService)) {
+        if (isKeyguardServiceEnabled()) {
             setShowingLocked(!shouldWaitForProvisioning()
                     && !mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
@@ -957,6 +960,15 @@
         mUpdateMonitor.dispatchFinishedGoingToSleep(why);
     }
 
+    private boolean isKeyguardServiceEnabled() {
+        try {
+            return mContext.getPackageManager().getServiceInfo(
+                    new ComponentName(mContext, KeyguardService.class), 0).isEnabled();
+        } catch (NameNotFoundException e) {
+            return true;
+        }
+    }
+
     private long getLockTimeout(int userId) {
         // if the screen turned off because of timeout or the user hit the power button
         // and we don't need to lock immediately, set an alarm
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 857c50f..b74ca28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -62,6 +62,10 @@
         visibleChangedListeners.add(listener)
     }
 
+    fun removeVisibilityChangeListener(listener: (Boolean) -> Unit) {
+        visibleChangedListeners.remove(listener)
+    }
+
     /**
      * Initialize this MediaObject and create a host view.
      * All state should already be set on this host before calling this method in order to avoid
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 78939df..8a9a6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -105,6 +105,7 @@
         window.setAttributes(lp);
         window.setContentView(mDialogView);
         window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        window.setWindowAnimations(R.style.Animation_MediaOutputDialog);
 
         mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
         mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a27e9ac..8c66ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.power;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -334,10 +336,14 @@
     }
 
     private PendingIntent pendingBroadcast(String action) {
-        return PendingIntent.getBroadcastAsUser(mContext, 0,
-                new Intent(action).setPackage(mContext.getPackageName())
-                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
-                0, UserHandle.CURRENT);
+        return PendingIntent.getBroadcastAsUser(
+                mContext,
+                0 /* request code */,
+                new Intent(action)
+                        .setPackage(mContext.getPackageName())
+                        .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+                FLAG_IMMUTABLE /* flags */,
+                UserHandle.CURRENT);
     }
 
     private static Intent settings(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index 6ac1e70..4f577f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -25,8 +25,7 @@
 import com.android.systemui.qs.TileLayout.exactly
 
 class DoubleLineTileLayout(
-    context: Context,
-    private val uiEventLogger: UiEventLogger
+    context: Context
 ) : ViewGroup(context), QSPanel.QSTileLayout {
 
     companion object {
@@ -84,7 +83,7 @@
         return false
     }
 
-    override fun setListening(listening: Boolean) {
+    override fun setListening(listening: Boolean, uiEventLogger: UiEventLogger) {
         if (_listening == listening) return
         _listening = listening
         for (record in mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 8c7d459..addbd5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -142,7 +142,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void setListening(boolean listening, UiEventLogger uiEventLogger) {
         if (mListening == listening) return;
         mListening = listening;
         updateListening();
@@ -150,7 +150,7 @@
 
     private void updateListening() {
         for (TilePage tilePage : mPages) {
-            tilePage.setListening(tilePage.getParent() == null ? false : mListening);
+            tilePage.setListening(tilePage.getParent() != null && mListening);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index f4571d7..16e9590 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -62,7 +61,7 @@
     private float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private View mDragHandle;
-    private View mQSPanelContainer;
+    private NonInterceptingScrollView mQSPanelContainer;
 
     private View mBackground;
 
@@ -127,18 +126,12 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
         // bottom and footer are inside the screen.
-        Configuration config = getResources().getConfiguration();
-        boolean navBelow = config.smallestScreenWidthDp >= 600
-                || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
         MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
 
         // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
         // subtract its height. We do not care if the collapsed notifications fit in the screen.
         int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
                 - getPaddingBottom();
-        if (navBelow) {
-            maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
-        }
 
         int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
                 + layoutParams.rightMargin;
@@ -184,7 +177,8 @@
         mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
     }
 
-    void updateResources(QSPanelController qsPanelController) {
+    void updateResources(QSPanelController qsPanelController,
+            QuickStatusBarHeaderController quickStatusBarHeaderController) {
         LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
         layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.quick_qs_offset_height);
@@ -196,7 +190,7 @@
         boolean marginsChanged = padding != mContentPadding;
         mContentPadding = padding;
         if (marginsChanged) {
-            updatePaddingsAndMargins(qsPanelController);
+            updatePaddingsAndMargins(qsPanelController, quickStatusBarHeaderController);
         }
     }
 
@@ -217,12 +211,13 @@
 
     public void updateExpansion(boolean animate) {
         int height = calculateContainerHeight();
+        int scrollBottom = calculateContainerBottom();
         setBottom(getTop() + height);
-        mQSDetail.setBottom(getTop() + height);
+        mQSDetail.setBottom(getTop() + scrollBottom);
         // Pin the drag handle to the bottom of the panel.
-        mDragHandle.setTranslationY(height - mDragHandle.getHeight());
+        mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
         mBackground.setTop(mQSPanelContainer.getTop());
-        updateBackgroundBottom(height, animate);
+        updateBackgroundBottom(scrollBottom, animate);
     }
 
     private void updateBackgroundBottom(int height, boolean animated) {
@@ -246,13 +241,23 @@
                 + mHeader.getHeight();
     }
 
+    int calculateContainerBottom() {
+        int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+        return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
+                : Math.round(mQsExpansion
+                        * (heightOverride + mQSPanelContainer.getScrollRange()
+                                - mQSPanelContainer.getScrollY() - mHeader.getHeight()))
+                        + mHeader.getHeight();
+    }
+
     public void setExpansion(float expansion) {
         mQsExpansion = expansion;
         mDragHandle.setAlpha(1.0f - expansion);
         updateExpansion();
     }
 
-    private void updatePaddingsAndMargins(QSPanelController qsPanelController) {
+    private void updatePaddingsAndMargins(QSPanelController qsPanelController,
+            QuickStatusBarHeaderController quickStatusBarHeaderController) {
         for (int i = 0; i < getChildCount(); i++) {
             View view = getChildAt(i);
             if (view == mQSCustomizer) {
@@ -271,7 +276,7 @@
             } else if (view == mHeader) {
                 // The header contains the QQS panel which needs to have special padding, to
                 // visually align them.
-                mHeader.setContentMargins(mContentPadding, mContentPadding);
+                quickStatusBarHeaderController.setContentMargins(mContentPadding, mContentPadding);
             } else {
                 view.setPaddingRelative(
                         mContentPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 27d3221..3638395 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -35,7 +35,7 @@
             new ConfigurationController.ConfigurationListener() {
         @Override
         public void onConfigChanged(Configuration newConfig) {
-            mView.updateResources(mQsPanelController);
+            mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
         }
     };
 
@@ -60,7 +60,7 @@
 
     @Override
     protected void onViewAttached() {
-        mView.updateResources(mQsPanelController);
+        mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
         mQsPanelController.setMediaVisibilityChangedListener((visible) -> {
             if (mQsPanelController.isShown()) {
                 mView.onMediaVisibilityChanged(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 043f5f1..dbdd04a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -300,7 +300,7 @@
                 ? View.VISIBLE
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
-                || (mQsExpanded && !mStackScrollerOverscrolling));
+                || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
         mFooter.setVisibility(
                 !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
                         || mShowCollapsedOnKeyguard)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 8955a7e..87a8da0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,8 +16,6 @@
 
 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;
 
 import android.annotation.NonNull;
@@ -36,30 +34,20 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.RemeasuringLinearLayout;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.media.MediaHierarchyManager;
-import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.animation.DisappearParameters;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /** View that represents the quick settings tile panel (when expanded/pulled down). **/
 public class QSPanel extends LinearLayout implements Tunable {
 
@@ -69,7 +57,6 @@
     private static final String TAG = "QSPanel";
 
     protected final Context mContext;
-    protected final MediaHost mMediaHost;
 
     /**
      * The index where the content starts that needs to be moved between parents
@@ -82,7 +69,6 @@
     protected BrightnessSlider mToggleSliderController;
 
     private final H mHandler = new H();
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     /** Whether or not the QS media player feature is enabled. */
     protected boolean mUsingMediaPlayer;
     private int mVisualMarginStart;
@@ -92,8 +78,6 @@
     protected boolean mListening;
 
     private QSDetail.Callback mCallback;
-    private final QSLogger mQSLogger;
-    protected final UiEventLogger mUiEventLogger;
     protected QSTileHost mHost;
     private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
             new ArrayList<>();
@@ -109,7 +93,6 @@
     @Nullable
     private ViewGroup mHeaderContainer;
     private PageIndicator mFooterPageIndicator;
-    private boolean mGridContentVisible = true;
     private int mContentMarginStart;
     private int mContentMarginEnd;
     private int mVisualTilePadding;
@@ -130,27 +113,12 @@
     private int mFooterMarginStartHorizontal;
     private Consumer<Boolean> mMediaVisibilityChangedListener;
 
-
-    @Inject
-    public QSPanel(
-            @Named(VIEW_CONTEXT) Context context,
-            AttributeSet attrs,
-            QSLogger qsLogger,
-            @Named(QS_PANEL) MediaHost mediaHost,
-            UiEventLogger uiEventLogger
-    ) {
+    public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUsingMediaPlayer = useQsMediaPlayer(context);
         mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.quick_settings_bottom_margin_media);
-        mMediaHost = mediaHost;
-        mMediaHost.addVisibilityChangeListener((visible) -> {
-            onMediaVisibilityChanged(visible);
-            return null;
-        });
         mContext = context;
-        mQSLogger = qsLogger;
-        mUiEventLogger = uiEventLogger;
 
         setOrientation(VERTICAL);
 
@@ -179,10 +147,7 @@
 
             lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
             addView(mHorizontalLinearLayout, lp);
-
-            initMediaHostState();
         }
-        mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), "");
     }
 
     protected void onMediaVisibilityChanged(Boolean visible) {
@@ -220,43 +185,6 @@
         return createRegularTileLayout();
     }
 
-    protected void initMediaHostState() {
-        mMediaHost.setExpansion(1.0f);
-        mMediaHost.setShowsOnlyActiveMedia(false);
-        updateMediaDisappearParameters();
-        mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
-    }
-
-    /**
-     * Update the way the media disappears based on if we're using the horizontal layout
-     */
-    private void updateMediaDisappearParameters() {
-        if (!mUsingMediaPlayer) {
-            return;
-        }
-        DisappearParameters parameters = mMediaHost.getDisappearParameters();
-        if (mUsingHorizontalLayout) {
-            // Only height remaining
-            parameters.getDisappearSize().set(0.0f, 0.4f);
-            // Disappearing on the right side on the bottom
-            parameters.getGonePivot().set(1.0f, 1.0f);
-            // translating a bit horizontal
-            parameters.getContentTranslationFraction().set(0.25f, 1.0f);
-            parameters.setDisappearEnd(0.6f);
-        } else {
-            // Only width remaining
-            parameters.getDisappearSize().set(1.0f, 0.0f);
-            // Disappearing on the bottom
-            parameters.getGonePivot().set(0.0f, 1.0f);
-            // translating a bit vertical
-            parameters.getContentTranslationFraction().set(0.0f, 1.05f);
-            parameters.setDisappearEnd(0.95f);
-        }
-        parameters.setFadeStartPosition(0.95f);
-        parameters.setDisappearStart(0.0f);
-        mMediaHost.setDisappearParameters(parameters);
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mTileLayout instanceof PagedTileLayout) {
@@ -298,14 +226,6 @@
         setMeasuredDimension(getMeasuredWidth(), height);
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mTileLayout != null) {
-            mTileLayout.setListening(false);
-        }
-        super.onDetachedFromWindow();
-    }
-
     protected String getDumpableTag() {
         return TAG;
     }
@@ -417,65 +337,6 @@
         mDivider = findViewById(R.id.divider);
     }
 
-    boolean switchTileLayout(boolean force, List<QSPanelControllerBase.TileRecord> records) {
-        /** Whether or not the QuickQSPanel currently contains a media player. */
-        boolean horizontal = shouldUseHorizontalLayout();
-        if (mDivider != null) {
-            if (!horizontal && mUsingMediaPlayer && mMediaHost.getVisible()) {
-                mDivider.setVisibility(View.VISIBLE);
-            } else {
-                mDivider.setVisibility(View.GONE);
-            }
-        }
-        if (horizontal != mUsingHorizontalLayout || force) {
-            mUsingHorizontalLayout = horizontal;
-            View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout;
-            View hiddenView = horizontal ? (View) mRegularTileLayout : mHorizontalLinearLayout;
-            ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
-            QSTileLayout newLayout = horizontal ? mHorizontalTileLayout : mRegularTileLayout;
-            if (hiddenView != null &&
-                    (mRegularTileLayout != mHorizontalTileLayout ||
-                            hiddenView != mRegularTileLayout)) {
-                // Only hide the view if the horizontal and the regular view are different,
-                // otherwise its reattached.
-                hiddenView.setVisibility(View.GONE);
-            }
-            visibleView.setVisibility(View.VISIBLE);
-            switchAllContentToParent(newParent, newLayout);
-            reAttachMediaHost();
-            if (mTileLayout != null) {
-                mTileLayout.setListening(false);
-                for (QSPanelControllerBase.TileRecord record : records) {
-                    mTileLayout.removeTile(record);
-                    record.tile.removeCallback(record.callback);
-                }
-            }
-            mTileLayout = newLayout;
-            if (needsDynamicRowsAndColumns()) {
-                newLayout.setMinRows(horizontal ? 2 : 1);
-                // Let's use 3 columns to match the current layout
-                newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS);
-            }
-            updateTileLayoutMargins();
-            updateFooterMargin();
-            updateDividerMargin();
-            updateMediaDisappearParameters();
-            updateMediaHostContentMargins();
-            updateHorizontalLinearLayoutMargins();
-            updatePadding();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Sets the listening state of the current layout to the state of the view. Used after
-     * switching layouts.
-     */
-    public void reSetLayoutListening() {
-        mTileLayout.setListening(mListening);
-    }
-
     private void updateHorizontalLinearLayoutMargins() {
         if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
             LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
@@ -538,27 +399,19 @@
         }
     }
 
-    private boolean shouldUseHorizontalLayout() {
-        return mUsingMediaPlayer && mMediaHost.getVisible()
-                && getResources().getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    protected void reAttachMediaHost() {
+    /** Call when orientation has changed and MediaHost needs to be adjusted. */
+    private void reAttachMediaHost(ViewGroup hostView, boolean horizontal) {
         if (!mUsingMediaPlayer) {
             return;
         }
-        boolean horizontal = shouldUseHorizontalLayout();
-        ViewGroup host = mMediaHost.getHostView();
-
         ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
-        ViewGroup currentParent = (ViewGroup) host.getParent();
+        ViewGroup currentParent = (ViewGroup) hostView.getParent();
         if (currentParent != newParent) {
             if (currentParent != null) {
-                currentParent.removeView(host);
+                currentParent.removeView(hostView);
             }
-            newParent.addView(host);
-            LinearLayout.LayoutParams layoutParams = (LayoutParams) host.getLayoutParams();
+            newParent.addView(hostView);
+            LinearLayout.LayoutParams layoutParams = (LayoutParams) hostView.getLayoutParams();
             layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
             layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
             layoutParams.weight = horizontal ? 1.2f : 0;
@@ -572,7 +425,6 @@
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
-        mQSLogger.logPanelExpanded(expanded, getDumpableTag());
         mExpanded = expanded;
         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
@@ -590,16 +442,10 @@
     }
 
     /** */
-    public void setListening(boolean listening, String cachedSpecs) {
-        if (mListening == listening) return;
+    public void setListening(boolean listening) {
         mListening = listening;
-        if (mTileLayout != null) {
-            mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), cachedSpecs);
-            mTileLayout.setListening(listening);
-        }
     }
 
-
     public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
         int xInWindow = locationInWindow[0];
         int yInWindow = locationInWindow[1];
@@ -742,14 +588,6 @@
         fireScanStateChanged(scanState);
     }
 
-    void setGridContentVisibility(boolean visible) {
-        int newVis = visible ? VISIBLE : INVISIBLE;
-        setVisibility(newVis);
-        if (mGridContentVisible != visible) {
-            mMetricsLogger.visibility(MetricsEvent.QS_PANEL, newVis);
-        }
-        mGridContentVisible = visible;
-    }
     private void fireShowingDetail(DetailAdapter detail, int x, int y) {
         if (mCallback != null) {
             mCallback.onShowingDetail(detail, x, y);
@@ -777,14 +615,15 @@
         return mDivider;
     }
 
-    public void setContentMargins(int startMargin, int endMargin) {
+    /** */
+    public void setContentMargins(int startMargin, int endMargin, ViewGroup mediaHostView) {
         // Only some views actually want this content padding, others want to go all the way
         // to the edge like the brightness slider
         mContentMarginStart = startMargin;
         mContentMarginEnd = endMargin;
         updateTileLayoutMargins(mContentMarginStart - mVisualTilePadding,
                 mContentMarginEnd - mVisualTilePadding);
-        updateMediaHostContentMargins();
+        updateMediaHostContentMargins(mediaHostView);
         updateFooterMargin();
         updateDividerMargin();
     }
@@ -840,13 +679,13 @@
     /**
      * Update the margins of the media hosts
      */
-    protected void updateMediaHostContentMargins() {
+    protected void updateMediaHostContentMargins(ViewGroup mediaHostView) {
         if (mUsingMediaPlayer) {
             int marginStart = mContentMarginStart;
             if (mUsingHorizontalLayout) {
                 marginStart = 0;
             }
-            updateMargins(mMediaHost.getHostView(), marginStart, mContentMarginEnd);
+            updateMargins(mediaHostView, marginStart, mContentMarginEnd);
         }
     }
 
@@ -885,6 +724,45 @@
         mSecurityFooter = view;
     }
 
+    void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
+            UiEventLogger uiEventLogger) {
+        if (horizontal != mUsingHorizontalLayout || force) {
+            mUsingHorizontalLayout = horizontal;
+            View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout;
+            View hiddenView = horizontal ? (View) mRegularTileLayout : mHorizontalLinearLayout;
+            ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
+            QSPanel.QSTileLayout newLayout = horizontal
+                    ? mHorizontalTileLayout : mRegularTileLayout;
+            if (hiddenView != null
+                    && (mRegularTileLayout != mHorizontalTileLayout
+                    || hiddenView != mRegularTileLayout)) {
+                // Only hide the view if the horizontal and the regular view are different,
+                // otherwise its reattached.
+                hiddenView.setVisibility(View.GONE);
+            }
+            visibleView.setVisibility(View.VISIBLE);
+            switchAllContentToParent(newParent, newLayout);
+            reAttachMediaHost(mediaHostView, horizontal);
+            mTileLayout = newLayout;
+            newLayout.setListening(mListening, uiEventLogger);
+            if (needsDynamicRowsAndColumns()) {
+                newLayout.setMinRows(horizontal ? 2 : 1);
+                // Let's use 3 columns to match the current layout
+                newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS);
+            }
+            updateMargins(mediaHostView);
+        }
+    }
+
+    private void updateMargins(ViewGroup mediaHostView) {
+        updateTileLayoutMargins();
+        updateFooterMargin();
+        updateDividerMargin();
+        updateMediaHostContentMargins(mediaHostView);
+        updateHorizontalLinearLayoutMargins();
+        updatePadding();
+    }
+
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
@@ -926,7 +804,7 @@
         boolean updateResources();
 
         /** */
-        void setListening(boolean listening);
+        void setListening(boolean listening, UiEventLogger uiEventLogger);
 
         /**
          * Set the minimum number of rows to show
@@ -938,7 +816,7 @@
         }
 
         /**
-         * Set the max number of collums to show
+         * Set the max number of columns to show
          *
          * @param maxColumns the maximum
          *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 32c81af..8ee284b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import android.annotation.NonNull;
 import android.content.res.Configuration;
@@ -27,12 +28,15 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.brightness.BrightnessController;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -56,6 +60,9 @@
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
     private final BrightnessSlider mBrightnessSlider;
 
+    private BrightnessMirrorController mBrightnessMirrorController;
+    private boolean mGridContentVisible = true;
+
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
         @Override
@@ -68,7 +75,6 @@
             updateBrightnessMirror();
         }
     };
-    private BrightnessMirrorController mBrightnessMirrorController;
 
     private final BrightnessMirrorController.BrightnessMirrorListener mBrightnessMirrorListener =
             mirror -> updateBrightnessMirror();
@@ -76,13 +82,14 @@
     @Inject
     QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService,
             QSTileHost qstileHost, QSCustomizerController qsCustomizerController,
+            @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QS_PANEL) MediaHost mediaHost,
             QSTileRevealController.Factory qsTileRevealControllerFactory,
             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
-            BrightnessController.Factory brightnessControllerFactory,
+            QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSlider.Factory brightnessSliderFactory) {
-        super(view, qstileHost, qsCustomizerController, mediaHost, metricsLogger, uiEventLogger,
-                dumpManager);
+        super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
+                metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mQsSecurityFooter = qsSecurityFooter;
         mTunerService = tunerService;
         mQsCustomizerController = qsCustomizerController;
@@ -99,6 +106,9 @@
     @Override
     public void onInit() {
         super.onInit();
+        mMediaHost.setExpansion(1);
+        mMediaHost.setShowsOnlyActiveMedia(false);
+        mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
         mQsCustomizerController.init();
         mBrightnessSlider.init();
     }
@@ -106,6 +116,9 @@
     @Override
     protected void onViewAttached() {
         super.onViewAttached();
+
+        updateMediaDisappearParameters();
+
         mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
         mView.updateResources();
         if (mView.isListening()) {
@@ -135,11 +148,6 @@
         super.onViewDetached();
     }
 
-    /** TODO(b/168904199): Remove this method once view is controllerized. */
-    QSPanel getView() {
-        return mView;
-    }
-
     /**
      * Set the header container of quick settings.
      */
@@ -239,7 +247,12 @@
 
     /** */
     public void setGridContentVisibility(boolean visible) {
-        mView.setGridContentVisibility(visible);
+        int newVis = visible ? View.VISIBLE : View.INVISIBLE;
+        setVisibility(newVis);
+        if (mGridContentVisible != visible) {
+            mMetricsLogger.visibility(MetricsEvent.QS_PANEL, newVis);
+        }
+        mGridContentVisible = visible;
     }
 
     public boolean isLayoutRtl() {
@@ -270,7 +283,7 @@
 
     /** */
     public void setContentMargins(int startMargin, int endMargin) {
-        mView.setContentMargins(startMargin, endMargin);
+        mView.setContentMargins(startMargin, endMargin, mMediaHost.getHostView());
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 06bf9ac..4418a74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,10 +17,12 @@
 package com.android.systemui.qs;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import android.content.ComponentName;
 import android.content.res.Configuration;
 import android.metrics.LogMaker;
+import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
@@ -31,7 +33,9 @@
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.animation.DisappearParameters;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -39,6 +43,11 @@
 import java.util.Collection;
 import java.util.stream.Collectors;
 
+import javax.inject.Named;
+
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
 /**
  * Controller for QSPanel views.
  *
@@ -48,9 +57,11 @@
         implements Dumpable{
     protected final QSTileHost mHost;
     private final QSCustomizerController mQsCustomizerController;
-    private final MediaHost mMediaHost;
-    private final MetricsLogger mMetricsLogger;
+    private final boolean mUsingMediaPlayer;
+    protected final MediaHost mMediaHost;
+    protected final MetricsLogger mMetricsLogger;
     private final UiEventLogger mUiEventLogger;
+    private final QSLogger mQSLogger;
     private final DumpManager mDumpManager;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
 
@@ -72,33 +83,49 @@
                 }
             };
 
+    private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
+        mView.onMediaVisibilityChanged(visible);
+        switchTileLayout(false);
+        return null;
+    };
+
+    private boolean mUsingHorizontalLayout;
+
     protected QSPanelControllerBase(T view, QSTileHost host,
-            QSCustomizerController qsCustomizerController, MediaHost mediaHost,
-            MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
+            QSCustomizerController qsCustomizerController,
+            @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, MediaHost mediaHost,
+            MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
+            DumpManager dumpManager) {
         super(view);
         mHost = host;
         mQsCustomizerController = qsCustomizerController;
+        mUsingMediaPlayer = usingMediaPlayer;
         mMediaHost = mediaHost;
         mMetricsLogger = metricsLogger;
         mUiEventLogger = uiEventLogger;
+        mQSLogger = qsLogger;
         mDumpManager = dumpManager;
     }
 
     @Override
+    protected void onInit() {
+        mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
+    }
+
+    @Override
     protected void onViewAttached() {
         mQsTileRevealController = createTileRevealController();
         if (mQsTileRevealController != null) {
             mQsTileRevealController.setExpansion(mRevealExpansion);
         }
 
+        mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
+        mView.onMediaVisibilityChanged(mMediaHost.getVisible());
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
         mHost.addCallback(mQSHostCallback);
-        mMediaHost.addVisibilityChangeListener(aBoolean -> {
-            switchTileLayout(false);
-            return null;
-        });
         setTiles();
         switchTileLayout(true);
+
         mDumpManager.registerDumpable(mView.getDumpableTag(), this);
     }
 
@@ -107,6 +134,10 @@
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
         mHost.removeCallback(mQSHostCallback);
 
+        mView.getTileLayout().setListening(false, mUiEventLogger);
+
+        mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
+
         for (TileRecord record : mRecords) {
             record.tile.removeCallbacks();
         }
@@ -195,6 +226,11 @@
 
     /** */
     public void setExpanded(boolean expanded) {
+        if (mView.isExpanded() == expanded) {
+            return;
+        }
+        mQSLogger.logPanelExpanded(expanded, mView.getDumpableTag());
+
         mView.setExpanded(expanded);
         mMetricsLogger.visibility(MetricsEvent.QS_PANEL, expanded);
         if (!expanded) {
@@ -228,17 +264,77 @@
 
 
     void setListening(boolean listening) {
-        mView.setListening(listening, mCachedSpecs);
+        mView.setListening(listening);
+
+        if (mView.getTileLayout() != null) {
+            mQSLogger.logAllTilesChangeListening(listening, mView.getDumpableTag(), mCachedSpecs);
+            mView.getTileLayout().setListening(listening, mUiEventLogger);
+        }
     }
 
     boolean switchTileLayout(boolean force) {
-        if (mView.switchTileLayout(force, mRecords)) {
+        /** Whether or not the QuickQSPanel currently contains a media player. */
+        boolean horizontal = shouldUseHorizontalLayout();
+        if (mView.getDivider() != null) {
+            if (!horizontal && mUsingMediaPlayer && mMediaHost.getVisible()) {
+                mView.getDivider().setVisibility(View.VISIBLE);
+            } else {
+                mView.getDivider().setVisibility(View.GONE);
+            }
+        }
+        if (horizontal != mUsingHorizontalLayout || force) {
+            mUsingHorizontalLayout = horizontal;
+            for (QSPanelControllerBase.TileRecord record : mRecords) {
+                mView.removeTile(record);
+                record.tile.removeCallback(record.callback);
+            }
+            mView.setUsingHorizontalLayout(mUsingHorizontalLayout, mMediaHost.getHostView(), force,
+                    mUiEventLogger);
+            updateMediaDisappearParameters();
+
             setTiles();
+
             return true;
         }
         return false;
     }
 
+    /**
+     * Update the way the media disappears based on if we're using the horizontal layout
+     */
+    void updateMediaDisappearParameters() {
+        if (!mUsingMediaPlayer) {
+            return;
+        }
+        DisappearParameters parameters = mMediaHost.getDisappearParameters();
+        if (mUsingHorizontalLayout) {
+            // Only height remaining
+            parameters.getDisappearSize().set(0.0f, 0.4f);
+            // Disappearing on the right side on the bottom
+            parameters.getGonePivot().set(1.0f, 1.0f);
+            // translating a bit horizontal
+            parameters.getContentTranslationFraction().set(0.25f, 1.0f);
+            parameters.setDisappearEnd(0.6f);
+        } else {
+            // Only width remaining
+            parameters.getDisappearSize().set(1.0f, 0.0f);
+            // Disappearing on the bottom
+            parameters.getGonePivot().set(0.0f, 1.0f);
+            // translating a bit vertical
+            parameters.getContentTranslationFraction().set(0.0f, 1.05f);
+            parameters.setDisappearEnd(0.95f);
+        }
+        parameters.setFadeStartPosition(0.95f);
+        parameters.setDisappearStart(0.0f);
+        mMediaHost.setDisappearParameters(parameters);
+    }
+
+    boolean shouldUseHorizontalLayout() {
+        return mUsingMediaPlayer && mMediaHost.getVisible()
+                && getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
+    }
+
     private void logTiles() {
         for (int i = 0; i < mRecords.size(); i++) {
             QSTile tile = mRecords.get(i).tile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 06e8634..c89f8e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,9 +16,6 @@
 
 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;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -29,15 +26,9 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
-import com.android.systemui.media.MediaHierarchyManager;
-import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.logging.QSLogger;
-
-import javax.inject.Inject;
-import javax.inject.Named;
 
 /**
  * Version of QSPanel that only shows N Quick Tiles in the QS Header.
@@ -52,15 +43,8 @@
     private boolean mDisabledByPolicy;
     private int mMaxTiles;
 
-
-    @Inject
-    public QuickQSPanel(
-            @Named(VIEW_CONTEXT) Context context,
-            AttributeSet attrs,
-            QSLogger qsLogger,
-            @Named(QUICK_QS_PANEL) MediaHost mediaHost,
-            UiEventLogger uiEventLogger) {
-        super(context, attrs, qsLogger, mediaHost, uiEventLogger);
+    public QuickQSPanel(Context context, AttributeSet attrs) {
+        super(context, attrs);
         mMaxTiles = Math.min(DEFAULT_MAX_TILES,
                 getResources().getInteger(R.integer.quick_qs_panel_max_columns));
         applyBottomMargin((View) mRegularTileLayout);
@@ -80,20 +64,12 @@
 
     @Override
     public TileLayout createRegularTileLayout() {
-        return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger);
+        return new QuickQSPanel.HeaderTileLayout(mContext);
     }
 
     @Override
     protected QSTileLayout createHorizontalTileLayout() {
-        return new DoubleLineTileLayout(mContext, mUiEventLogger);
-    }
-
-    @Override
-
-    protected void initMediaHostState() {
-        mMediaHost.setExpansion(0.0f);
-        mMediaHost.setShowsOnlyActiveMedia(true);
-        mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+        return new DoubleLineTileLayout(mContext);
     }
 
     @Override
@@ -207,13 +183,10 @@
 
     private static class HeaderTileLayout extends TileLayout {
 
-        private final UiEventLogger mUiEventLogger;
-
         private Rect mClippingBounds = new Rect();
 
-        public HeaderTileLayout(Context context, UiEventLogger uiEventLogger) {
+        HeaderTileLayout(Context context) {
             super(context);
-            mUiEventLogger = uiEventLogger;
             setClipChildren(false);
             setClipToPadding(false);
             LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
@@ -341,14 +314,14 @@
         }
 
         @Override
-        public void setListening(boolean listening) {
+        public void setListening(boolean listening, UiEventLogger uiEventLogger) {
             boolean startedListening = !mListening && listening;
-            super.setListening(listening);
+            super.setListening(listening, uiEventLogger);
             if (startedListening) {
                 // getNumVisibleTiles() <= mRecords.size()
                 for (int i = 0; i < getNumVisibleTiles(); i++) {
                     QSTile tile = mRecords.get(i).tile;
-                    mUiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
+                    uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
                             tile.getMetricsSpec(), tile.getInstanceId());
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 7f50eef..cca0e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,15 +17,18 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.logging.QSLogger;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -37,8 +40,6 @@
 @QSScope
 public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
 
-    private List<QSTile> mAllTiles = new ArrayList<>();
-
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             newConfig -> {
                 int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -50,11 +51,20 @@
     @Inject
     QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
             QSCustomizerController qsCustomizerController,
+            @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
-            MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+            MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
             DumpManager dumpManager) {
-        super(view, qsTileHost, qsCustomizerController, mediaHost, metricsLogger, uiEventLogger,
-                dumpManager);
+        super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
+                uiEventLogger, qsLogger, dumpManager);
+    }
+
+    @Override
+    protected void onInit() {
+        super.onInit();
+        mMediaHost.setExpansion(0.0f);
+        mMediaHost.setShowsOnlyActiveMedia(true);
+        mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
     }
 
     @Override
@@ -80,14 +90,19 @@
 
     @Override
     public void setTiles() {
-        mAllTiles.clear();
+        List<QSTile> tiles = new ArrayList();
         for (QSTile tile : mHost.getTiles()) {
-            mAllTiles.add(tile);
-            if (mAllTiles.size() == QuickQSPanel.DEFAULT_MAX_TILES) {
+            tiles.add(tile);
+            if (tiles.size() == mView.getNumQuickTiles()) {
                 break;
             }
         }
-        super.setTiles(mAllTiles.subList(0, mView.getNumQuickTiles()), true);
+        super.setTiles(tiles, true);
+    }
+
+    /** */
+    public void setContentMargins(int marginStart, int marginEnd) {
+        mView.setContentMargins(marginStart, marginEnd, mMediaHost.getHostView());
     }
 
     public int getNumQuickTiles() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 09894e5..b85ad81 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -51,8 +51,6 @@
 import com.android.systemui.DualToneHandler;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.privacy.OngoingPrivacyChip;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
@@ -313,10 +311,11 @@
                 .build();
     }
 
-    public void setExpanded(boolean expanded) {
+    /** */
+    public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
-        mHeaderQsPanel.setExpanded(expanded);
+        quickQSPanelController.setExpanded(expanded);
         updateEverything();
     }
 
@@ -466,14 +465,15 @@
     }
 
     /** */
-    public void setContentMargins(int marginStart, int marginEnd) {
+    public void setContentMargins(int marginStart, int marginEnd,
+            QuickQSPanelController quickQSPanelController) {
         mContentMarginStart = marginStart;
         mContentMarginEnd = marginEnd;
         for (int i = 0; i < getChildCount(); i++) {
             View view = getChildAt(i);
             if (view == mHeaderQsPanel) {
                 // QS panel doesn't lays out some of its content full width
-                mHeaderQsPanel.setContentMargins(marginStart, marginEnd);
+                quickQSPanelController.setContentMargins(marginStart, marginEnd);
             } else {
                 MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
                 lp.setMarginStart(marginStart);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 5ee9df4..c5b76fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -379,6 +379,11 @@
                 mZenModeController.getConsolidatedPolicy());
     }
 
+    public void setContentMargins(int contentPaddingStart, int contentPaddingEnd) {
+        mView.setContentMargins(contentPaddingStart, contentPaddingEnd, mHeaderQsPanelController);
+    }
+
+
     private static class ClockDemoModeReceiver implements DemoMode {
         private Clock mClockView;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 4ab7afd..348dca5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -9,6 +9,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
@@ -59,8 +60,12 @@
         return getTop();
     }
 
-    @Override
     public void setListening(boolean listening) {
+        setListening(listening, null);
+    }
+
+    @Override
+    public void setListening(boolean listening, UiEventLogger uiEventLogger) {
         if (mListening == listening) return;
         mListening = listening;
         for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index f3bf306..2363aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.qs.dagger;
 
+import static com.android.systemui.util.Utils.useQsMediaPlayer;
+
+import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -44,6 +47,7 @@
 @Module
 public interface QSFragmentModule {
     String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
+    String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
 
     /** */
     @Provides
@@ -108,4 +112,12 @@
     static View providesQSSecurityFooterView(LayoutInflater layoutInflater, QSPanel qsPanel) {
         return layoutInflater.inflate(R.layout.quick_settings_footer, qsPanel, false);
     }
+
+    /** */
+    @Provides
+    @Named(QS_USING_MEDIA_PLAYER)
+    static boolean providesQSUsingMediaPlayer(Context context) {
+        return useQsMediaPlayer(context);
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 216b83f..84818ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -41,6 +41,7 @@
 public class NotificationHeaderUtil {
 
     private static final TextViewComparator sTextViewComparator = new TextViewComparator();
+    private static final TextViewComparator sAppNameComparator = new AppNameComparator();
     private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator();
     private static final VisibilityApplicator sAppNameApplicator = new AppNameApplicator();
     private static  final DataExtractor sIconExtractor = new DataExtractor() {
@@ -119,7 +120,7 @@
                 mRow,
                 com.android.internal.R.id.app_name_text,
                 null,
-                sTextViewComparator,
+                sAppNameComparator,
                 sAppNameApplicator));
         mComparators.add(HeaderProcessor.forTextView(mRow,
                 com.android.internal.R.id.header_text));
@@ -389,4 +390,17 @@
             super.apply(parent, view, apply, reset);
         }
     }
+
+    private static class AppNameComparator extends TextViewComparator {
+        @Override
+        public boolean compare(View parent, View child, Object parentData, Object childData) {
+            if (isEmpty(child)) {
+                // In headerless notifications the AppName view exists but is usually GONE (and not
+                // populated).  We need to treat this case as equal to the header in order to
+                // deduplicate the view.
+                return true;
+            }
+            return super.compare(parent, child, parentData, childData);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 41ce51c..0b79387 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -23,32 +23,44 @@
 
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.phone.DoubleTapHelper;
+import com.android.systemui.statusbar.phone.NotificationTapHelper;
+import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
 
 /**
  * Controller for {@link ActivatableNotificationView}
  */
-public class ActivatableNotificationViewController {
-    private final ActivatableNotificationView mView;
+public class ActivatableNotificationViewController
+        extends ViewController<ActivatableNotificationView> {
     private final ExpandableOutlineViewController mExpandableOutlineViewController;
     private final AccessibilityManager mAccessibilityManager;
     private final FalsingManager mFalsingManager;
-    private DoubleTapHelper mDoubleTapHelper;
-    private boolean mNeedsDimming;
+    private final NotificationTapHelper mNotificationTapHelper;
+    private final TouchHandler mTouchHandler = new TouchHandler();
 
-    private TouchHandler mTouchHandler = new TouchHandler();
+    private boolean mNeedsDimming;
 
     @Inject
     public ActivatableNotificationViewController(ActivatableNotificationView view,
+            NotificationTapHelper.Factory notificationTapHelpFactory,
             ExpandableOutlineViewController expandableOutlineViewController,
             AccessibilityManager accessibilityManager, FalsingManager falsingManager) {
-        mView = view;
+        super(view);
         mExpandableOutlineViewController = expandableOutlineViewController;
         mAccessibilityManager = accessibilityManager;
         mFalsingManager = falsingManager;
 
+        mNotificationTapHelper = notificationTapHelpFactory.create(
+                (active) -> {
+                    if (active) {
+                        mView.makeActive();
+                        mFalsingManager.onNotificationActive();
+                    } else {
+                        mView.makeInactive(true /* animate */);
+                    }
+                }, mView::performClick, mView::handleSlideBack);
+
         mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() {
             @Override
             public void onActivated(ActivatableNotificationView view) {
@@ -64,25 +76,25 @@
     /**
      * Initialize the controller, setting up handlers and other behavior.
      */
-    public void init() {
+    @Override
+    public void onInit() {
         mExpandableOutlineViewController.init();
-        mDoubleTapHelper = new DoubleTapHelper(mView, (active) -> {
-            if (active) {
-                mView.makeActive();
-                mFalsingManager.onNotificationActive();
-            } else {
-                mView.makeInactive(true /* animate */);
-            }
-        }, mView::performClick, mView::handleSlideBack,
-                mFalsingManager::onNotificationDoubleTap);
         mView.setOnTouchListener(mTouchHandler);
         mView.setTouchHandler(mTouchHandler);
-        mView.setOnDimmedListener(dimmed -> {
-            mNeedsDimming = dimmed;
-        });
+        mView.setOnDimmedListener(dimmed -> mNeedsDimming = dimmed);
         mView.setAccessibilityManager(mAccessibilityManager);
     }
 
+    @Override
+    protected void onViewAttached() {
+
+    }
+
+    @Override
+    protected void onViewDetached() {
+
+    }
+
     class TouchHandler implements Gefingerpoken, View.OnTouchListener {
         private boolean mBlockNextTouch;
 
@@ -103,7 +115,7 @@
                     // let's ensure we have a ripple
                     return false;
                 }
-                result = mDoubleTapHelper.onTouchEvent(ev, mView.getActualHeight());
+                result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight());
             } else {
                 return false;
             }
@@ -117,7 +129,7 @@
                     && !mAccessibilityManager.isTouchExplorationEnabled()) {
                 if (!mView.isActive()) {
                     return true;
-                } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
+                } else if (mFalsingManager.isFalseDoubleTap()) {
                     mBlockNextTouch = true;
                     mView.makeInactive(true /* animate */);
                     return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index ea39064..5682c88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -666,8 +666,12 @@
             } else {
                 smallHeight = mMaxSmallHeightBeforeS;
             }
-        } else if (isMediaLayout && showCompactMediaSeekbar) {
-            smallHeight = mMaxSmallHeightMedia;
+        } else if (isMediaLayout) {
+            // TODO(b/172652345): MediaStyle notifications currently look broken when we enforce
+            //  the standard notification height, so we have to afford them more vertical space to
+            //  make sure we don't crop them terribly.  We actually need to revisit this and give
+            //  them a headerless design, then remove this hack.
+            smallHeight = showCompactMediaSeekbar ? mMaxSmallHeightMedia : mMaxSmallHeightBeforeS;
         } else if (isMessagingLayout) {
             // TODO(b/173204301): MessagingStyle notifications currently look broken when we enforce
             //  the standard notification height, so we have to afford them more vertical space to
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 3287745..74e6c00 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
@@ -1369,16 +1369,8 @@
             bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
             bubbleButton.setVisibility(VISIBLE);
             actionContainer.setVisibility(VISIBLE);
-
-            int paddingEnd = getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.bubble_visible_padding_end);
-            actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0);
         } else  {
             bubbleButton.setVisibility(GONE);
-
-            int paddingEnd = getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.bubble_gone_padding_end);
-            actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 447fa43..1034b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -295,7 +295,7 @@
             mMenuContainer = new FrameLayout(mContext);
         }
         final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0) == 1;
+                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1;
         if (newFlowHideShelf) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index d228ce1..05db67d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -56,6 +56,7 @@
 
     private CachingIconView mIcon;
     private NotificationExpandButton mExpandButton;
+    private View mAltExpandTarget;
     protected NotificationHeaderView mNotificationHeader;
     protected NotificationTopLineView mNotificationTopLine;
     private TextView mHeaderText;
@@ -106,6 +107,7 @@
         mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
         mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
+        mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target);
         mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
@@ -260,6 +262,9 @@
     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
         mExpandButton.setOnClickListener(expandable ? onClickListener : null);
+        if (mAltExpandTarget != null) {
+            mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null);
+        }
         if (mNotificationHeader != null) {
             mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 8050fea..885048d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -41,7 +41,6 @@
     private static final float MAX_PULSE_HEIGHT = 100000f;
 
     private final SectionProvider mSectionProvider;
-    private ArrayList<View> mDraggedViews = new ArrayList<>();
     private int mScrollY;
     private int mAnchorViewIndex;
     private int mAnchorViewY;
@@ -161,19 +160,6 @@
         mAnchorViewY = anchorViewY;
     }
 
-    /** Call when dragging begins. */
-    public void onBeginDrag(View view) {
-        mDraggedViews.add(view);
-    }
-
-    public void onDragFinished(View view) {
-        mDraggedViews.remove(view);
-    }
-
-    public ArrayList<View> getDraggedViews() {
-        return mDraggedViews;
-    }
-
     /**
      * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
      *               translucent and everything is scaled back a bit.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index fbcfef3..742e517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -353,31 +353,24 @@
     private boolean mContinuousShadowUpdate;
     private boolean mContinuousBackgroundUpdate;
     private ViewTreeObserver.OnPreDrawListener mShadowUpdater
-            = new ViewTreeObserver.OnPreDrawListener() {
-
-        @Override
-        public boolean onPreDraw() {
-            updateViewShadows();
-            return true;
-        }
-    };
+            = () -> {
+                updateViewShadows();
+                return true;
+            };
     private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
                 updateBackground();
                 return true;
             };
-    private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
-        @Override
-        public int compare(ExpandableView view, ExpandableView otherView) {
-            float endY = view.getTranslationY() + view.getActualHeight();
-            float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
-            if (endY < otherEndY) {
-                return -1;
-            } else if (endY > otherEndY) {
-                return 1;
-            } else {
-                // The two notifications end at the same location
-                return 0;
-            }
+    private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> {
+        float endY = view.getTranslationY() + view.getActualHeight();
+        float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
+        if (endY < otherEndY) {
+            return -1;
+        } else if (endY > otherEndY) {
+            return 1;
+        } else {
+            // The two notifications end at the same location
+            return 0;
         }
     };
     private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
@@ -415,8 +408,6 @@
      */
     private float mBackgroundXFactor = 1f;
 
-    private boolean mSwipingInProgress;
-
     private boolean mUsingLightTheme;
     private boolean mQsExpanded;
     private boolean mForwardScrollable;
@@ -834,9 +825,9 @@
         if (!mShouldDrawNotificationBackground) {
             return;
         }
-        final boolean clearUndershelf = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0 /* show background by default */) == 1;
-        if (clearUndershelf) {
+        final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1;
+        if (newFlowHideShelf) {
             mBackgroundPaint.setColor(Color.TRANSPARENT);
             invalidate();
             return;
@@ -2554,12 +2545,13 @@
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() != View.GONE && !(child instanceof StackScrollerDecorView)
-                    && child != mShelf) {
+            if (child.getVisibility() != View.GONE
+                    && !(child instanceof StackScrollerDecorView)
+                    && child != mShelf
+                    && mSwipeHelper.getSwipedView() != child) {
                 children.add(child);
             }
         }
-
         return children;
     }
 
@@ -3577,7 +3569,10 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onGenericMotionEvent(MotionEvent event) {
-        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
+        if (!isScrollingEnabled()
+                || !mIsExpanded
+                || mSwipeHelper.isSwiping()
+                || mExpandingNotification
                 || mDisallowScrollingInThisMotion) {
             return false;
         }
@@ -4077,14 +4072,6 @@
         return false;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    void setSwipingInProgress(boolean swiping) {
-        mSwipingInProgress = swiping;
-        if (swiping) {
-            requestDisallowInterceptTouchEvent(true);
-        }
-    }
-
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -4127,9 +4114,8 @@
             mStatusBar.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
-            if (draggedViews.size() > 0) {
-                draggedViews.clear();
+            if (mSwipeHelper.isSwiping()) {
+                mSwipeHelper.resetSwipeState();
                 updateContinuousShadowDrawing();
             }
         }
@@ -5196,15 +5182,11 @@
             ExpandableView child = (ExpandableView) getTransientView(i);
             child.dump(fd, pw, args);
         }
-        ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
-        int draggedCount = draggedViews.size();
-        pw.println("  Dragged Views: " + draggedCount);
-        for (int i = 0; i < draggedCount; i++) {
-            View view = draggedViews.get(i);
-            if (view instanceof ExpandableView) {
-                ExpandableView expandableView = (ExpandableView) view;
-                expandableView.dump(fd, pw, args);
-            }
+        View swipedView = mSwipeHelper.getSwipedView();
+        pw.println("  Swiped view: " + swipedView);
+        if (swipedView instanceof ExpandableView) {
+            ExpandableView expandableView = (ExpandableView) swipedView;
+            expandableView.dump(fd, pw, args);
         }
     }
 
@@ -5522,12 +5504,16 @@
         mSwipedOutViews.add(v);
     }
 
-    void addDraggedView(View view) {
-        mAmbientState.onBeginDrag(view);
+    void onSwipeBegin() {
+        requestDisallowInterceptTouchEvent(true);
+        updateFirstAndLastBackgroundViews();
+        updateContinuousShadowDrawing();
+        updateContinuousBackgroundDrawing();
+        requestChildrenUpdate();
     }
 
-    void removeDraggedView(View view) {
-        mAmbientState.onDragFinished(view);
+    void onSwipeEnd() {
+        updateFirstAndLastBackgroundViews();
     }
 
     void setTopHeadsUpEntry(NotificationEntry topEntry) {
@@ -5539,10 +5525,6 @@
         mAmbientState.setHasAlertEntries(numHeadsUp > 0);
     }
 
-    boolean getSwipingInProgress() {
-        return mSwipingInProgress;
-    }
-
     public boolean getIsExpanded() {
         return mIsExpanded;
     }
@@ -5583,10 +5565,6 @@
         mTouchHandler = touchHandler;
     }
 
-    boolean isSwipingInProgress() {
-        return mSwipingInProgress;
-    }
-
     boolean getCheckSnoozeLeaveBehind() {
         return mCheckForLeavebehind;
     }
@@ -5661,9 +5639,17 @@
         mSectionsManager.updateSectionBoundaries(reason);
     }
 
+    boolean isSilkDismissEnabled() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* enabled by default */) == 1;
+    }
+
     void updateContinuousBackgroundDrawing() {
+        if (isSilkDismissEnabled()) {
+            return;
+        }
         boolean continuousBackground = !mAmbientState.isFullyAwake()
-                && !mAmbientState.getDraggedViews().isEmpty();
+                && mSwipeHelper.isSwiping();
         if (continuousBackground != mContinuousBackgroundUpdate) {
             mContinuousBackgroundUpdate = continuousBackground;
             if (continuousBackground) {
@@ -5677,7 +5663,7 @@
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void updateContinuousShadowDrawing() {
         boolean continuousShadowUpdate = mAnimationRunning
-                || !mAmbientState.getDraggedViews().isEmpty();
+                || mSwipeHelper.isSwiping();
         if (continuousShadowUpdate != mContinuousShadowUpdate) {
             if (continuousShadowUpdate) {
                 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7cee365..006d8da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -361,7 +361,6 @@
 
                 @Override
                 public void onDragCancelled(View v) {
-                    mView.setSwipingInProgress(false);
                     mFalsingManager.onNotificationStopDismissing();
                 }
 
@@ -392,14 +391,10 @@
                  */
 
                 public void handleChildViewDismissed(View view) {
-                    mView.setSwipingInProgress(false);
                     if (mView.getDismissAllInProgress()) {
                         return;
                     }
-
-                    mView.removeDraggedView(view);
-                    mView.updateContinuousShadowDrawing();
-
+                    mView.onSwipeEnd();
                     if (view instanceof ExpandableNotificationRow) {
                         ExpandableNotificationRow row = (ExpandableNotificationRow) view;
                         if (row.isHeadsUp()) {
@@ -454,18 +449,12 @@
                 @Override
                 public void onBeginDrag(View v) {
                     mFalsingManager.onNotificationStartDismissing();
-                    mView.setSwipingInProgress(true);
-                    mView.addDraggedView(v);
-                    mView.updateContinuousShadowDrawing();
-                    mView.updateContinuousBackgroundDrawing();
-                    mView.requestChildrenUpdate();
+                    mView.onSwipeBegin();
                 }
 
                 @Override
                 public void onChildSnappedBack(View animView, float targetLeft) {
-                    mView.addDraggedView(animView);
-                    mView.updateContinuousShadowDrawing();
-                    mView.updateContinuousBackgroundDrawing();
+                    mView.onSwipeEnd();
                     if (animView instanceof ExpandableNotificationRow) {
                         ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
                         if (row.isPinned() && !canChildBeDismissed(row)
@@ -1536,12 +1525,12 @@
 
             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
             boolean expandWantsIt = false;
-            boolean swipingInProgress = mView.isSwipingInProgress();
-            if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) {
+            if (!mSwipeHelper.isSwiping()
+                    && !mView.getOnlyScrollingInThisMotion() && guts == null) {
                 expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
             }
             boolean scrollWantsIt = false;
-            if (!swipingInProgress && !mView.isExpandingNotification()) {
+            if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
                 scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
             }
             boolean swipeWantsIt = false;
@@ -1582,10 +1571,9 @@
                     || ev.getActionMasked() == MotionEvent.ACTION_UP;
             mView.handleEmptySpaceClick(ev);
             boolean expandWantsIt = false;
-            boolean swipingInProgress = mView.getSwipingInProgress();
             boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
             boolean expandingNotification = mView.isExpandingNotification();
-            if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion
+            if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
                     && guts == null) {
                 ExpandHelper expandHelper = mView.getExpandHelper();
                 if (isCancelOrUp) {
@@ -1600,7 +1588,7 @@
                 }
             }
             boolean scrollerWantsIt = false;
-            if (mView.isExpanded() && !swipingInProgress && !expandingNotification
+            if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
                     && !mView.getDisallowScrollingInThisMotion()) {
                 scrollerWantsIt = mView.onScrollTouch(ev);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
deleted file mode 100644
index 78ea5c0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ /dev/null
@@ -1,176 +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.statusbar.phone;
-
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.R;
-
-/**
- * Detects a double tap.
- */
-public class DoubleTapHelper {
-
-    private static final long DOUBLETAP_TIMEOUT_MS = 1200;
-
-    private final View mView;
-    private final ActivationListener mActivationListener;
-    private final DoubleTapListener mDoubleTapListener;
-    private final SlideBackListener mSlideBackListener;
-    private final DoubleTapLogListener mDoubleTapLogListener;
-
-    private float mTouchSlop;
-    private float mDoubleTapSlop;
-
-    private boolean mActivated;
-
-    private float mDownX;
-    private float mDownY;
-    private boolean mTrackTouch;
-
-    private float mActivationX;
-    private float mActivationY;
-    private Runnable mTapTimeoutRunnable = this::makeInactive;
-
-    public DoubleTapHelper(View view, ActivationListener activationListener,
-            DoubleTapListener doubleTapListener, SlideBackListener slideBackListener,
-            DoubleTapLogListener doubleTapLogListener) {
-        mTouchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
-        mDoubleTapSlop = view.getResources().getDimension(R.dimen.double_tap_slop);
-        mView = view;
-
-        mActivationListener = activationListener;
-        mDoubleTapListener = doubleTapListener;
-        mSlideBackListener = slideBackListener;
-        mDoubleTapLogListener = doubleTapLogListener;
-    }
-
-    public boolean onTouchEvent(MotionEvent event) {
-        return onTouchEvent(event, Integer.MAX_VALUE);
-    }
-
-    public boolean onTouchEvent(MotionEvent event, int maxTouchableHeight) {
-        int action = event.getActionMasked();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mDownX = event.getX();
-                mDownY = event.getY();
-                mTrackTouch = true;
-                if (mDownY > maxTouchableHeight) {
-                    mTrackTouch = false;
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                if (!isWithinTouchSlop(event)) {
-                    makeInactive();
-                    mTrackTouch = false;
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (isWithinTouchSlop(event)) {
-                    if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
-                        return true;
-                    }
-                    if (!mActivated) {
-                        makeActive();
-                        mView.postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
-                        mActivationX = event.getX();
-                        mActivationY = event.getY();
-                    } else {
-                        boolean withinDoubleTapSlop = isWithinDoubleTapSlop(event);
-                        if (mDoubleTapLogListener != null) {
-                            mDoubleTapLogListener.onDoubleTapLog(withinDoubleTapSlop,
-                                    event.getX() - mActivationX,
-                                    event.getY() - mActivationY);
-                        }
-                        if (withinDoubleTapSlop) {
-                            makeInactive();
-                            if (!mDoubleTapListener.onDoubleTap()) {
-                                return false;
-                            }
-                        } else {
-                            makeInactive();
-                            mTrackTouch = false;
-                        }
-                    }
-                } else {
-                    makeInactive();
-                    mTrackTouch = false;
-                }
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                makeInactive();
-                mTrackTouch = false;
-                break;
-            default:
-                break;
-        }
-        return mTrackTouch;
-    }
-
-    private void makeActive() {
-        if (!mActivated) {
-            mActivated = true;
-            mActivationListener.onActiveChanged(true);
-        }
-    }
-
-    private void makeInactive() {
-        if (mActivated) {
-            mActivated = false;
-            mActivationListener.onActiveChanged(false);
-            mView.removeCallbacks(mTapTimeoutRunnable);
-        }
-    }
-
-    private boolean isWithinTouchSlop(MotionEvent event) {
-        return Math.abs(event.getX() - mDownX) < mTouchSlop
-                && Math.abs(event.getY() - mDownY) < mTouchSlop;
-    }
-
-    public boolean isWithinDoubleTapSlop(MotionEvent event) {
-        if (!mActivated) {
-            // If we're not activated there's no double tap slop to satisfy.
-            return true;
-        }
-
-        return Math.abs(event.getX() - mActivationX) < mDoubleTapSlop
-                && Math.abs(event.getY() - mActivationY) < mDoubleTapSlop;
-    }
-
-    @FunctionalInterface
-    public interface ActivationListener {
-        void onActiveChanged(boolean active);
-    }
-
-    @FunctionalInterface
-    public interface DoubleTapListener {
-        boolean onDoubleTap();
-    }
-
-    @FunctionalInterface
-    public interface SlideBackListener {
-        boolean onSlideBack();
-    }
-
-    @FunctionalInterface
-    public interface DoubleTapLogListener {
-        void onDoubleTapLog(boolean accepted, float dx, float dy);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index c936e82..6b88811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -926,6 +926,8 @@
         if (mUpdateMonitor.isUdfpsEnrolled()) {
             availableSpace = mNotificationStackScrollLayoutController.getHeight()
                     - minPadding - shelfSize
+                    - mKeyguardStatusViewController.getOwnerInfoHeight()
+                    - mKeyguardStatusViewController.getLogoutButtonHeight()
                     - (mStatusBar.getDisplayHeight() - mAuthController.getUdfpsRegion().top);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
new file mode 100644
index 0000000..50c8e2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -0,0 +1,163 @@
+/*
+ * 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.statusbar.phone;
+
+import android.view.MotionEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+
+/**
+ * Detects single and double taps on notifications.
+ */
+public class NotificationTapHelper {
+
+    public static final long DOUBLE_TAP_TIMEOUT_MS = 1200;
+
+    private final ActivationListener mActivationListener;
+    private final DoubleTapListener mDoubleTapListener;
+    private final FalsingManager mFalsingManager;
+    private final DelayableExecutor mExecutor;
+    private final SlideBackListener mSlideBackListener;
+
+    private boolean mTrackTouch;
+    private Runnable mTimeoutCancel;
+
+    private NotificationTapHelper(FalsingManager falsingManager, DelayableExecutor executor,
+            ActivationListener activationListener, DoubleTapListener doubleTapListener,
+            SlideBackListener slideBackListener) {
+        mFalsingManager = falsingManager;
+        mExecutor = executor;
+        mActivationListener = activationListener;
+        mDoubleTapListener = doubleTapListener;
+        mSlideBackListener = slideBackListener;
+    }
+
+    @VisibleForTesting
+    boolean onTouchEvent(MotionEvent event) {
+        return onTouchEvent(event, Integer.MAX_VALUE);
+    }
+
+    /** Call to have the helper process a touch event. */
+    public boolean onTouchEvent(MotionEvent event, int maxTouchableHeight) {
+        int action = event.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mTrackTouch = event.getY() <= maxTouchableHeight;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (mTrackTouch && mFalsingManager.isFalseTap(false)) {
+                    makeInactive();
+                    mTrackTouch = false;
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                makeInactive();
+                mTrackTouch = false;
+                break;
+            case MotionEvent.ACTION_UP:
+                mTrackTouch = false;
+
+                // 1) See if we have confidence that we can activate after a single tap.
+                // 2) Else, see if it looks like a tap at all and check for a double-tap.
+                if (!mFalsingManager.isFalseTap(true)) {
+                    makeInactive();
+                    return mDoubleTapListener.onDoubleTap();
+                } else if (!mFalsingManager.isFalseTap(false)) {
+                    if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
+                        return true;
+                    }
+                    if (mTimeoutCancel == null) {
+                        // first tap
+                        makeActive();
+                        return true;
+                    } else {
+                        // second tap
+                        makeInactive();
+                        if (!mFalsingManager.isFalseDoubleTap()) {
+                            return mDoubleTapListener.onDoubleTap();
+                        }
+                    }
+                } else {
+                    makeInactive();
+                }
+                break;
+            default:
+                break;
+        }
+        return mTrackTouch;
+    }
+
+    private void makeActive() {
+        mTimeoutCancel = mExecutor.executeDelayed(this::makeInactive, DOUBLE_TAP_TIMEOUT_MS);
+        mActivationListener.onActiveChanged(true);
+    }
+
+    private void makeInactive() {
+        mActivationListener.onActiveChanged(false);
+        if (mTimeoutCancel != null) {
+            mTimeoutCancel.run();
+            mTimeoutCancel = null;
+        }
+    }
+
+    /** */
+    @FunctionalInterface
+    public interface ActivationListener {
+        /** */
+        void onActiveChanged(boolean active);
+    }
+
+    /** */
+    @FunctionalInterface
+    public interface DoubleTapListener {
+        /** */
+        boolean onDoubleTap();
+    }
+
+    /** */
+    @FunctionalInterface
+    public interface SlideBackListener {
+        /** */
+        boolean onSlideBack();
+    }
+
+    /**
+     * Injectable factory that creates a {@link NotificationTapHelper}.
+     */
+    public static class Factory {
+        private final FalsingManager mFalsingManager;
+        private final DelayableExecutor mDelayableExecutor;
+
+        @Inject
+        public Factory(FalsingManager falsingManager, @Main DelayableExecutor delayableExecutor) {
+            mFalsingManager = falsingManager;
+            mDelayableExecutor = delayableExecutor;
+        }
+
+        /** Create a {@link NotificationTapHelper} */
+        public NotificationTapHelper create(ActivationListener activationListener,
+                DoubleTapListener doubleTapListener, SlideBackListener slideBackListener) {
+            return new NotificationTapHelper(mFalsingManager, mDelayableExecutor,
+                    activationListener, doubleTapListener, slideBackListener);
+        }
+    }
+}
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 5d84245..e6ac7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -147,7 +147,6 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.charging.WirelessChargingAnimation;
-import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.DemoMode;
@@ -1250,10 +1249,6 @@
                 message.write(SystemProperties.get("ro.serialno"));
                 message.write("\n");
 
-                PrintWriter falsingPw = new PrintWriter(message);
-                FalsingLog.dump(falsingPw);
-                falsingPw.flush();
-
                 startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
                                 .setType("*/*")
                                 .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
@@ -2676,9 +2671,6 @@
             mLightBarController.dump(fd, pw, args);
         }
 
-        mFalsingManager.dump(pw);
-        FalsingLog.dump(pw);
-
         pw.println("SharedPreferences:");
         for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
             pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f9dfd7a..b912614 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -26,13 +26,17 @@
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -160,6 +164,8 @@
     private boolean mPulsing;
     private boolean mGesturalNav;
     private boolean mIsDocked;
+    private boolean mIsPortraitMode;
+    private int mScreenWidthDp;
 
     protected boolean mFirstUpdate = true;
     protected boolean mLastShowing;
@@ -174,6 +180,7 @@
     private boolean mLastPulsing;
     private int mLastBiometricMode;
     private boolean mLastLockVisible;
+    private boolean mLastLockOrientationIsPortrait;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private Runnable mKeyguardGoneCancelAction;
@@ -263,6 +270,9 @@
         mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
         mStatusBarStateController.addCallback(this);
         mConfigurationController.addCallback(this);
+        mIsPortraitMode = mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_PORTRAIT;
+        mScreenWidthDp = mContext.getResources().getConfiguration().screenWidthDp;
         mGesturalNav = QuickStepContract.isGesturalMode(
                 mNavigationModeController.addListener(this));
         if (mDockManager != null) {
@@ -272,6 +282,18 @@
     }
 
     @Override
+    public void onDensityOrFontScaleChanged() {
+        hideBouncer(true /* destroyView */);
+    }
+
+    @Override
+    public void onConfigChanged(Configuration newConfig) {
+        mIsPortraitMode = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;
+        mScreenWidthDp = newConfig.screenWidthDp;
+        updateLockIcon();
+    }
+
+    @Override
     public void onPanelExpansionChanged(float expansion, boolean tracking) {
         // We don't want to translate the bounce when:
         // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
@@ -317,14 +339,32 @@
         if (mLockIconContainer == null) {
             return;
         }
+
         boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                 && !mNotificationPanelViewController.isQsExpanded();
         boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
                 && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway();
+        boolean orientationChange =
+                lockVisible && (mLastLockOrientationIsPortrait != mIsPortraitMode);
 
-        if (mLastLockVisible != lockVisible) {
+        if (mLastLockVisible != lockVisible || orientationChange) {
             mLastLockVisible = lockVisible;
+            mLastLockOrientationIsPortrait = mIsPortraitMode;
             if (lockVisible) {
+                FrameLayout.LayoutParams lp =
+                        (FrameLayout.LayoutParams) mLockIconContainer.getLayoutParams();
+                if (mIsPortraitMode) {
+                    lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+                } else {
+                    final int width = (int) TypedValue.applyDimension(
+                            TypedValue.COMPLEX_UNIT_DIP,
+                            mScreenWidthDp,
+                            mContext.getResources().getDisplayMetrics()) / 3;
+                    mLockIconContainer.setMinimumWidth(width);
+                    lp.gravity = Gravity.TOP | Gravity.LEFT;
+                }
+                mLockIconContainer.setLayoutParams(lp);
+
                 CrossFadeHelper.fadeIn(mLockIconContainer,
                         AppearAnimationUtils.DEFAULT_APPEAR_DURATION /* duration */,
                         0 /* delay */);
@@ -685,11 +725,6 @@
     }
 
     @Override
-    public void onDensityOrFontScaleChanged() {
-        hideBouncer(true /* destroyView */);
-    }
-
-    @Override
     public void onNavigationModeChanged(int mode) {
         boolean gesturalNav = QuickStepContract.isGesturalMode(mode);
         if (gesturalNav != mGesturalNav) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 4b4e1df..cdc5d87 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -24,8 +24,6 @@
 import android.view.View;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.lang.reflect.InvocationTargetException;
@@ -93,16 +91,6 @@
          * Creates the NotificationStackScrollLayout.
          */
         NotificationStackScrollLayout createNotificationStackScrollLayout();
-
-        /**
-         * Creates the QSPanel.
-         */
-        QSPanel createQSPanel();
-
-        /**
-         * Creates the QuickQSPanel.
-         */
-        QuickQSPanel createQuickQSPanel();
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 34be362..411fbc3 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -105,6 +105,7 @@
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
+    private KeyguardUpdateMonitorCallback mAppPairsKeyguardCallback;
 
     @Inject
     public WMShell(Context context, CommandQueue commandQueue,
@@ -144,6 +145,7 @@
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
+        mAppPairsOptional.ifPresent(this::initAppPairs);
     }
 
     @VisibleForTesting
@@ -292,6 +294,16 @@
         });
     }
 
+    void initAppPairs(AppPairs appPairs) {
+        mAppPairsKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+            @Override
+            public void onKeyguardVisibilityChanged(boolean showing) {
+                appPairs.onKeyguardVisibilityChanged(showing);
+            }
+        };
+        mKeyguardUpdateMonitor.registerCallback(mAppPairsKeyguardCallback);
+    }
+
     @Override
     public void writeToProto(SystemUiTraceProto proto) {
         if (proto.wmShell == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index ef8a08c..4505b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -84,8 +84,10 @@
     @WMSingleton
     @Provides
     static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
-            SyncTransactionQueue syncQueue) {
-        return new AppPairsController(shellTaskOrganizer, syncQueue);
+            SyncTransactionQueue syncQueue, DisplayController displayController,
+            TaskStackListenerImpl taskStackListener) {
+        return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
+                taskStackListener);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 777db95..5088a53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -211,6 +211,16 @@
     }
 
     @Test
+    public void testOnDialogAnimatedIn_sendsCancelReason_whenPendingDismiss() {
+        initializeContainer(Authenticators.BIOMETRIC_WEAK);
+        mAuthContainer.mContainerState = AuthContainerView.STATE_PENDING_DISMISS;
+        mAuthContainer.onDialogAnimatedIn();
+        verify(mCallback).onDismissed(
+                eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+                eq(null) /* credentialAttestation */);
+    }
+
+    @Test
     public void testLayoutParams_hasSecureWindowFlag() {
         final IBinder windowToken = mock(IBinder.class);
         final WindowManager.LayoutParams layoutParams =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
deleted file mode 100644
index c3c9ecc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ /dev/null
@@ -1,128 +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.classifier;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertThat;
-
-import android.provider.DeviceConfig;
-import android.testing.AndroidTestingRunner;
-import android.util.DisplayMetrics;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
-import com.android.systemui.classifier.brightline.FalsingDataProvider;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dock.DockManagerFake;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.sensors.ProximitySensor;
-import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.utils.leaks.FakeBatteryController;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.After;
-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)
-public class FalsingManagerProxyTest extends LeakCheckedTest {
-    @Mock(stubOnly = true)
-    PluginManager mPluginManager;
-    @Mock(stubOnly = true)
-    ProximitySensor mProximitySensor;
-    @Mock(stubOnly = true)
-    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock DumpManager mDumpManager;
-    private FalsingManagerProxy mProxy;
-    private DeviceConfigProxy mDeviceConfig;
-    private FalsingDataProvider mFalsingDataProvider;
-    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-    private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
-    private DockManager mDockManager = new DockManagerFake();
-    private StatusBarStateController mStatusBarStateController =
-            new StatusBarStateControllerImpl(new UiEventLoggerFake());
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mDeviceConfig = new DeviceConfigProxyFake();
-        mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-        mFalsingDataProvider = new FalsingDataProvider(
-                new DisplayMetrics(), new FakeBatteryController(getLeakCheck()));
-    }
-
-    @After
-    public void tearDown() {
-        if (mProxy != null) {
-            mProxy.cleanup();
-        }
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerDisabled() {
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
-                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
-                mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
-        assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerEnabled() throws InterruptedException {
-        mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mExecutor.runAllReady();
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
-                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
-                mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
-        assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerToggled() throws InterruptedException {
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
-                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
-                mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
-        assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-
-        mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mExecutor.runAllReady();
-        assertThat(mProxy.getInternalFalsingManager(),
-                instanceOf(BrightLineFalsingManager.class));
-
-        mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-        mExecutor.runAllReady();
-        assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 061664b..30dddc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -20,14 +20,18 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.DisplayMetrics;
+import android.view.ViewConfiguration;
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerFake;
 import com.android.systemui.statusbar.StatusBarState;
@@ -37,6 +41,7 @@
 import com.android.systemui.util.DeviceConfigProxyFake;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.utils.leaks.FakeBatteryController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -56,6 +61,10 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private ProximitySensor mProximitySensor;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private ViewConfiguration mViewConfiguration;
     private SysuiStatusBarStateController mStatusBarStateController;
     private FalsingDataProvider mFalsingDataProvider;
     private FakeBatteryController mFakeBatteryController;
@@ -71,14 +80,17 @@
         dm.ydpi = 100;
         dm.widthPixels = 100;
         dm.heightPixels = 100;
-        mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController);
+        mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController,
+                new FakeSystemClock());
         DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
         DockManager dockManager = new DockManagerFake();
         mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake());
         mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+        when(mResources.getDimension(R.dimen.double_tap_slop)).thenReturn(1f);
+        when(mViewConfiguration.getScaledTouchSlop()).thenReturn(1);
         mFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
-                mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,
-                mStatusBarStateController);
+                mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, mResources,
+                mViewConfiguration, dockManager, mStatusBarStateController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
index a4d198a..69d39fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
@@ -21,6 +21,7 @@
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.utils.leaks.FakeBatteryController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -44,7 +45,8 @@
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
         mFakeBatteryController = new FakeBatteryController(getLeakCheck());
-        mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
+        mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
+                new FakeSystemClock());
         mDataProvider.setInteractionType(UNLOCK);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java
new file mode 100644
index 0000000..9f3a1e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DoubleTapClassifierTest extends ClassifierTest {
+
+    private static final int TOUCH_SLOP = 100;
+    private static final long DOUBLE_TAP_TIMEOUT_MS = 100;
+
+    private List<MotionEvent> mMotionEvents = new ArrayList<>();
+    private final Deque<List<MotionEvent>> mHistoricalMotionEvents = new LinkedList<>();
+
+    @Mock
+    private FalsingDataProvider mDataProvider;
+    @Mock
+    private SingleTapClassifier mSingleTapClassifier;
+    private DoubleTapClassifier mClassifier;
+
+    @Before
+    public void setup() {
+        super.setup();
+        MockitoAnnotations.initMocks(this);
+        mClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, TOUCH_SLOP,
+                DOUBLE_TAP_TIMEOUT_MS);
+        doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+    }
+
+    @After
+    public void tearDown() {
+        for (MotionEvent motionEvent : mMotionEvents) {
+            motionEvent.recycle();
+        }
+
+        mMotionEvents.clear();
+        super.tearDown();
+    }
+
+
+    @Test
+    public void testSingleTap() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat("Single tap recognized as a valid double tap", result,  is(true));
+    }
+
+    @Test
+    public void testDoubleTap() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        archiveMotionEvents();
+
+        addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP, TOUCH_SLOP);
+        addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat(mClassifier.getReason(), result, is(false));
+    }
+
+    @Test
+    public void testBadFirstTap() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(false, true);
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        archiveMotionEvents();
+
+        addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat("Bad first touch allowed", result, is(true));
+    }
+
+    @Test
+    public void testBadSecondTap() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(true, false);
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        archiveMotionEvents();
+
+        addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat("Bad second touch allowed", result, is(true));
+    }
+
+    @Test
+    public void testBadTouchSlop() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        archiveMotionEvents();
+
+        addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP + 1, TOUCH_SLOP);
+        addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP + 1);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat("Sloppy second touch allowed", result, is(true));
+    }
+
+    @Test
+    public void testBadTouchSlow() {
+        when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        archiveMotionEvents();
+
+        addMotionEvent(DOUBLE_TAP_TIMEOUT_MS + 1, DOUBLE_TAP_TIMEOUT_MS + 1,
+                MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(DOUBLE_TAP_TIMEOUT_MS + 1, DOUBLE_TAP_TIMEOUT_MS + 2,
+                MotionEvent.ACTION_UP, 1, 1);
+
+        boolean result = mClassifier.isFalseTouch();
+        assertThat("Slow second tap allowed", result, is(true));
+    }
+
+    private void addMotionEvent(long downMs, long eventMs, int action, int x, int y) {
+        MotionEvent ev = MotionEvent.obtain(downMs, eventMs, action, x, y, 0);
+        mMotionEvents.add(ev);
+        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
+    }
+
+    private void archiveMotionEvents() {
+        mHistoricalMotionEvents.addFirst(mMotionEvents);
+        doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+        mMotionEvents = new ArrayList<>();
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index f13bc73..be38f4419 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -26,6 +26,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.utils.leaks.FakeBatteryController;
 
 import org.junit.After;
@@ -51,7 +52,8 @@
         displayMetrics.ydpi = 100;
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
-        mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
+        mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
+                new FakeSystemClock());
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java
new file mode 100644
index 0000000..642b077
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SingleTapClassifierTest extends ClassifierTest {
+
+    private static final int TOUCH_SLOP = 100;
+
+    private final List<MotionEvent> mMotionEvents = new ArrayList<>();
+
+    @Mock
+    private FalsingDataProvider mDataProvider;
+    private SingleTapClassifier mClassifier;
+
+    @Before
+    public void setup() {
+        super.setup();
+        MockitoAnnotations.initMocks(this);
+        mClassifier = new SingleTapClassifier(mDataProvider, TOUCH_SLOP);
+    }
+
+    @After
+    public void tearDown() {
+        for (MotionEvent motionEvent : mMotionEvents) {
+            motionEvent.recycle();
+        }
+
+        mMotionEvents.clear();
+        super.tearDown();
+    }
+
+
+    @Test
+    public void testSimpleTap_XSlop() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(false));
+
+        mMotionEvents.clear();
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP + 2, 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(false));
+
+    }
+
+    @Test
+    public void testSimpleTap_YSlop() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP);
+
+        assertThat(mClassifier.isFalseTouch(), is(false));
+
+        mMotionEvents.clear();
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP + 2);
+
+        assertThat(mClassifier.isFalseTouch(), is(false));
+    }
+
+
+    @Test
+    public void testFalseTap_XSlop() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP + 1, 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(true));
+
+        mMotionEvents.clear();
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP - 1, 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(true));
+
+    }
+
+    @Test
+    public void testFalseTap_YSlop() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(true));
+
+        mMotionEvents.clear();
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP - 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(true));
+    }
+
+    @Test
+    public void testLargeMovementFalses() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_MOVE, 1, TOUCH_SLOP + 1);
+        addMotionEvent(0, 2, MotionEvent.ACTION_UP, 1, 1);
+
+        assertThat(mClassifier.isFalseTouch(), is(true));
+    }
+
+    @Test
+    public void testDirectlySuppliedMotionEvents() {
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
+
+        assertThat(mClassifier.isTap(mMotionEvents), is(true));
+
+        addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
+        addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
+
+        assertThat(mClassifier.isTap(mMotionEvents), is(false));
+
+    }
+
+    private void addMotionEvent(long downMs, long eventMs, int action, int x, int y) {
+        MotionEvent ev = MotionEvent.obtain(downMs, eventMs, action, x, y, 0);
+        mMotionEvents.add(ev);
+        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java
new file mode 100644
index 0000000..1dfffb2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TimeLimitedMotionEventBufferTest extends SysuiTestCase {
+
+    private static final long MAX_AGE_MS = 100;
+
+    private TimeLimitedMotionEventBuffer mBuffer;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBuffer = new TimeLimitedMotionEventBuffer(MAX_AGE_MS);
+    }
+
+    @After
+    public void tearDown() {
+        for (MotionEvent motionEvent : mBuffer) {
+            motionEvent.recycle();
+        }
+        mBuffer.clear();
+    }
+
+    @Test
+    public void testAllEventsRetained() {
+        MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        mBuffer.add(eventA);
+        mBuffer.add(eventB);
+        mBuffer.add(eventC);
+        mBuffer.add(eventD);
+
+        assertThat(mBuffer.size(), is(4));
+    }
+
+    @Test
+    public void testOlderEventsRemoved() {
+        MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventC = MotionEvent.obtain(
+                0, MAX_AGE_MS + 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventD = MotionEvent.obtain(
+                0, MAX_AGE_MS + 2, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        mBuffer.add(eventA);
+        mBuffer.add(eventB);
+        assertThat(mBuffer.size(), is(2));
+
+        mBuffer.add(eventC);
+        mBuffer.add(eventD);
+        assertThat(mBuffer.size(), is(2));
+
+        assertThat(mBuffer.get(0), is(eventC));
+        assertThat(mBuffer.get(1), is(eventD));
+    }
+
+    @Test
+    public void testFullyExpired() {
+        MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        mBuffer.add(eventA);
+        mBuffer.add(eventB);
+        mBuffer.add(eventC);
+        mBuffer.add(eventD);
+
+        assertThat(mBuffer.isFullyExpired(2), is(false));
+        assertThat(mBuffer.isFullyExpired(6), is(true));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 0fe44ad..0dc268a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -42,7 +42,9 @@
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizerController;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.animation.DisappearParameters;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -75,6 +77,8 @@
     @Mock
     private MetricsLogger mMetricsLogger;
     private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+    @Mock
+    private QSLogger mQSLogger;
     private DumpManager mDumpManager = new DumpManager();
     @Mock
     QSTileImpl mQSTile;
@@ -89,9 +93,10 @@
     private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
         protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
                 QSCustomizerController qsCustomizerController, MediaHost mediaHost,
-                MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
-            super(view, host, qsCustomizerController, mediaHost,
-                    metricsLogger, uiEventLogger, dumpManager);
+                MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
+                DumpManager dumpManager) {
+            super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
+                    qsLogger, dumpManager);
         }
 
         @Override
@@ -109,13 +114,17 @@
         when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED);
         when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED);
         when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout);
+        when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
+        when(mQSTile.getTileSpec()).thenReturn("dnd");
         when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
         when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
         when(mQSTileRevealControllerFactory.create(any(), any()))
                 .thenReturn(mQSTileRevealController);
+        when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
 
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
-                mQSCustomizerController, mMediaHost, mMetricsLogger, mUiEventLogger, mDumpManager);
+                mQSCustomizerController, mMediaHost,
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
         mController.init();
         reset(mQSTileRevealController);
@@ -125,9 +134,9 @@
     public void testSetRevealExpansion_preAttach() {
         mController.onViewDetached();
 
-        QSPanelControllerBase<QSPanel> controller = new QSPanelControllerBase<QSPanel>(
-                mQSPanel, mQSTileHost, mQSCustomizerController, mMediaHost, mMetricsLogger,
-                mUiEventLogger, mDumpManager) {
+        QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
+                mQSTileHost, mQSCustomizerController, mMediaHost,
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
             @Override
             protected QSTileRevealController createTileRevealController() {
                 return mQSTileRevealController;
@@ -159,14 +168,18 @@
 
     @Test
     public void testSetExpanded_Metrics() {
+        when(mQSPanel.isExpanded()).thenReturn(false);
         mController.setExpanded(true);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
+        verify(mQSLogger).logPanelExpanded(true, mQSPanel.getDumpableTag());
         assertEquals(1, mUiEventLogger.numLogs());
         assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0));
         mUiEventLogger.getLogs().clear();
 
+        when(mQSPanel.isExpanded()).thenReturn(true);
         mController.setExpanded(false);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
+        verify(mQSLogger).logPanelExpanded(false, mQSPanel.getDumpableTag());
         assertEquals(1, mUiEventLogger.numLogs());
         assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0));
         mUiEventLogger.getLogs().clear();
@@ -195,4 +208,14 @@
         assertEquals(expected, w.getBuffer().toString());
     }
 
+    @Test
+    public void setListening() {
+        mController.setListening(true);
+        verify(mQSLogger).logAllTilesChangeListening(true, "QSPanel", "dnd");
+        verify(mPagedTileLayout).setListening(true, mUiEventLogger);
+
+        mController.setListening(false);
+        verify(mQSLogger).logAllTilesChangeListening(false, "QSPanel", "dnd");
+        verify(mPagedTileLayout).setListening(false, mUiEventLogger);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 9090b9b..a6c2d08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizerController;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.brightness.BrightnessController;
 import com.android.systemui.settings.brightness.BrightnessSlider;
@@ -77,6 +78,8 @@
     @Mock
     private QSSecurityFooter mQSSecurityFooter;
     @Mock
+    private QSLogger mQSLogger;
+    @Mock
     private BrightnessController.Factory mBrightnessControllerFactory;
     @Mock
     private BrightnessController mBrightnessController;
@@ -91,7 +94,6 @@
     @Mock
     PagedTileLayout mPagedTileLayout;
 
-
     private QSPanelController mController;
 
     @Before
@@ -112,9 +114,9 @@
         when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
 
         mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService,
-                mQSTileHost, mQSCustomizerController, mMediaHost, mQSTileRevealControllerFactory,
-                mDumpManager, mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory,
-                mToggleSliderViewControllerFactory);
+                mQSTileHost, mQSCustomizerController, true, mMediaHost,
+                mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
+                mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 450ffac..a726181 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -17,13 +17,10 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
-import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -32,18 +29,11 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.util.animation.DisappearParameters;
-import com.android.systemui.util.animation.UniqueObjectHostView;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,7 +48,6 @@
 @SmallTest
 public class QSPanelTest extends SysuiTestCase {
 
-    private MetricsLogger mMetricsLogger;
     private TestableLooper mTestableLooper;
     private QSPanel mQsPanel;
     @Mock
@@ -75,30 +64,23 @@
     @Mock
     private QSTileView mQSTileView;
     @Mock
-    private MediaHost mMediaHost;
-    @Mock
     private ActivityStarter mActivityStarter;
-    private UiEventLoggerFake mUiEventLogger;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mTestableLooper = TestableLooper.get(this);
 
-        // Dependencies for QSSecurityFooter
-        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
-        mDependency.injectMockDependency(SecurityController.class);
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-        mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
-        when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext()));
-        when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
+//        // Dependencies for QSSecurityFooter
+//        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
+//        mDependency.injectMockDependency(SecurityController.class);
+//        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+//        mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
         mDndTileRecord.tile = dndTile;
         mDndTileRecord.tileView = mQSTileView;
 
-        mUiEventLogger = new UiEventLoggerFake();
         mTestableLooper.runWithLooper(() -> {
-            mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-            mQsPanel = new QSPanel(mContext, null, mQSLogger, mMediaHost, mUiEventLogger);
+            mQsPanel = new QSPanel(mContext, null);
             mQsPanel.onFinishInflate();
             // Provides a parent with non-zero size for QSPanel
             mParentView = new FrameLayout(mContext);
@@ -113,15 +95,6 @@
     }
 
     @Test
-    public void testSetExpanded_Metrics() {
-        mQsPanel.setExpanded(true);
-        verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag());
-
-        mQsPanel.setExpanded(false);
-        verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag());
-    }
-
-    @Test
     public void testOpenDetailsWithExistingTile_NoException() {
         mTestableLooper.processAllMessages();
         mQsPanel.openDetails(dndTile);
@@ -131,15 +104,6 @@
     }
 
     @Test
-    public void setListening() {
-        mQsPanel.setListening(true, "dnd");
-        verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd");
-
-        mQsPanel.setListening(false, "dnd");
-        verify(mQSLogger).logAllTilesChangeListening(false, mQsPanel.getDumpableTag(), "dnd");
-    }
-
-    @Test
     public void testOpenDetailsWithNullParameter_NoException() {
         mTestableLooper.processAllMessages();
         mQsPanel.openDetails(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
new file mode 100644
index 0000000..3f2b4da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.qs
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.MediaHost
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.customize.QSCustomizerController
+import com.android.systemui.qs.logging.QSLogger
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QuickQSPanelControllerTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var quickQSPanel: QuickQSPanel
+    @Mock
+    private lateinit var qsTileHost: QSTileHost
+    @Mock
+    private lateinit var qsCustomizerController: QSCustomizerController
+    @Mock
+    private lateinit var mediaHost: MediaHost
+    @Mock
+    private lateinit var metricsLogger: MetricsLogger
+    private val uiEventLogger = UiEventLoggerFake()
+    @Mock
+    private lateinit var qsLogger: QSLogger
+    private val dumpManager = DumpManager()
+    @Mock
+    private lateinit var tile: QSTile
+    @Mock
+    private lateinit var tileLayout: TileLayout
+
+    private lateinit var controller: QuickQSPanelController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(quickQSPanel.tileLayout).thenReturn(tileLayout)
+        `when`(quickQSPanel.dumpableTag).thenReturn("")
+
+        controller = QuickQSPanelController(
+                quickQSPanel,
+                qsTileHost,
+                qsCustomizerController,
+                false,
+                mediaHost,
+                metricsLogger,
+                uiEventLogger,
+                qsLogger,
+                dumpManager
+        )
+
+        controller.init()
+    }
+
+    @After
+    fun tearDown() {
+        controller.onViewDetached()
+    }
+
+    @Test
+    fun testTileSublistWithFewerTiles_noCrash() {
+        `when`(quickQSPanel.numQuickTiles).thenReturn(3)
+
+        `when`(qsTileHost.tiles).thenReturn(listOf(tile, tile))
+
+        controller.setTiles()
+    }
+
+    @Test
+    fun testTileSublistWithTooManyTiles() {
+        val limit = 3
+        `when`(quickQSPanel.numQuickTiles).thenReturn(limit)
+        `when`(qsTileHost.tiles).thenReturn(listOf(tile, tile, tile, tile))
+
+        controller.setTiles()
+
+        verify(quickQSPanel, times(limit)).addTile(any())
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 6c7c20a..54cee84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -75,7 +75,7 @@
     public void testSetListening_CallsSetListeningOnTile() {
         QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
-        mTileLayout.setListening(true);
+        mTileLayout.setListening(true, null);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, true);
     }
 
@@ -83,14 +83,14 @@
     public void testSetListening_SameValueIsNoOp() {
         QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
-        mTileLayout.setListening(false);
+        mTileLayout.setListening(false, null);
         verify(tileRecord.tile, times(1)).setListening(any(), anyBoolean());
     }
 
     @Test
     public void testSetListening_ChangesValueForAddingFutureTiles() {
         QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
-        mTileLayout.setListening(true);
+        mTileLayout.setListening(true, null);
         mTileLayout.addTile(tileRecord);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, true);
     }
@@ -98,7 +98,7 @@
     @Test
     public void testRemoveTile_CallsSetListeningFalseOnTile() {
         QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
-        mTileLayout.setListening(true);
+        mTileLayout.setListening(true, null);
         mTileLayout.addTile(tileRecord);
         mTileLayout.removeTile(tileRecord);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, false);
@@ -108,7 +108,7 @@
     public void testRemoveAllViews_CallsSetListeningFalseOnAllTiles() {
         QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord();
         QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord();
-        mTileLayout.setListening(true);
+        mTileLayout.setListening(true, null);
         mTileLayout.addTile(tileRecord1);
         mTileLayout.addTile(tileRecord2);
         mTileLayout.removeAllViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 37e8218..c426c87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
+import static android.provider.Settings.Global.SHOW_NEW_NOTIF_DISMISS;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -96,6 +97,7 @@
     @Test
     public void testNoAppOpsInSlowSwipe() {
         Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0);
+        Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
 
         NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
         row.createMenu(mRow, null);
@@ -108,6 +110,7 @@
     @Test
     public void testNoSnoozeInSlowSwipe() {
         Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0);
+        Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
 
         NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
         row.createMenu(mRow, null);
@@ -120,6 +123,7 @@
     @Test
     public void testSnoozeInSlowSwipe() {
         Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 1);
+        Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
 
         NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
         row.createMenu(mRow, null);
@@ -130,6 +134,19 @@
     }
 
     @Test
+    public void testSlowSwipe_newDismiss() {
+        Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 1);
+        Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 1);
+
+        NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
+        row.createMenu(mRow, null);
+
+        ViewGroup container = (ViewGroup) row.getMenuView();
+        // Clear menu
+        assertEquals(0, container.getChildCount());
+    }
+
+    @Test
     public void testIsSnappedAndOnSameSide() {
         NotificationMenuRow row = Mockito.spy(
                 new NotificationMenuRow(mContext, mPeopleNotificationIdentifier));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index e9e6b3e..4841b3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -231,6 +231,7 @@
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
+        when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
         FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
                 mDisplayMetrics);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
similarity index 69%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
index df1233a..4ed2746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
@@ -16,17 +16,12 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
-import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 import android.view.View;
@@ -36,48 +31,47 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-public class DoubleTapHelperTest extends SysuiTestCase {
+public class NotificationTapHelperTest extends SysuiTestCase {
 
-    private DoubleTapHelper mDoubleTapHelper;
-    private int mTouchSlop;
-    private int mDoubleTouchSlop;
+    private NotificationTapHelper mNotificationTapHelper;
+    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+    private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
     @Mock private View mView;
-    @Mock private DoubleTapHelper.ActivationListener mActivationListener;
-    @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener;
-    @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener;
-    @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener;
+    @Mock private NotificationTapHelper.ActivationListener mActivationListener;
+    @Mock private NotificationTapHelper.DoubleTapListener mDoubleTapListener;
+    @Mock private NotificationTapHelper.SlideBackListener mSlideBackListener;
     @Mock private Resources mResources;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        // The double tap slop has to be less than the regular slop, otherwise it has no effect.
-        mDoubleTouchSlop = mTouchSlop - 1;
         when(mView.getContext()).thenReturn(mContext);
         when(mView.getResources()).thenReturn(mResources);
         when(mResources.getDimension(R.dimen.double_tap_slop))
-                .thenReturn((float) mDoubleTouchSlop);
+                .thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1);
 
-        mDoubleTapHelper = new DoubleTapHelper(mView,
-                                               mActivationListener,
-                                               mDoubleTapListener,
-                                               mSlideBackListener, mDoubleTapLogListener);
+        mFalsingManager.setFalseRobustTap(true);  // Test double tapping most of the time.
+
+        mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor)
+                .create(mActivationListener, mDoubleTapListener, mSlideBackListener);
     }
 
     @Test
     public void testDoubleTap_success() {
-        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeA = 100;
         long downtimeB = downtimeA + 100;
 
         MotionEvent evDownA = MotionEvent.obtain(downtimeA,
@@ -105,16 +99,13 @@
                                                1,
                                                0);
 
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener).onActiveChanged(true);
-        verify(mView).postDelayed(any(Runnable.class), anyLong());
-        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
         verify(mDoubleTapListener, never()).onDoubleTap();
 
-        mDoubleTapHelper.onTouchEvent(evDownB);
-        mDoubleTapHelper.onTouchEvent(evUpB);
-        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        mNotificationTapHelper.onTouchEvent(evDownB);
+        mNotificationTapHelper.onTouchEvent(evUpB);
         verify(mDoubleTapListener).onDoubleTap();
 
         evDownA.recycle();
@@ -125,7 +116,7 @@
 
     @Test
     public void testSingleTap_timeout() {
-        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeA = 100;
 
         MotionEvent evDownA = MotionEvent.obtain(downtimeA,
                                                  downtimeA,
@@ -140,21 +131,19 @@
                                                1,
                                                0);
 
-        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener).onActiveChanged(true);
-        verify(mView).postDelayed(runnableCaptor.capture(), anyLong());
-        runnableCaptor.getValue().run();
-        verify(mActivationListener).onActiveChanged(true);
+        drainExecutor();
+        verify(mActivationListener).onActiveChanged(false);
 
         evDownA.recycle();
         evUpA.recycle();
     }
 
     @Test
-    public void testSingleTap_slop() {
-        long downtimeA = SystemClock.uptimeMillis();
+    public void testSingleTap_falsed() {
+        long downtimeA = 100;
 
         MotionEvent evDownA = MotionEvent.obtain(downtimeA,
                                                  downtimeA,
@@ -165,13 +154,13 @@
         MotionEvent evUpA = MotionEvent.obtain(downtimeA,
                                                downtimeA + 1,
                                                MotionEvent.ACTION_UP,
-                                               1 + mTouchSlop,
+                                               1,
                                                1,
                                                0);
 
-        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
+        mFalsingManager.setFalseTap(true);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener, never()).onActiveChanged(true);
         verify(mDoubleTapListener, never()).onDoubleTap();
 
@@ -180,8 +169,8 @@
     }
 
     @Test
-    public void testDoubleTap_slop() {
-        long downtimeA = SystemClock.uptimeMillis();
+    public void testDoubleTap_falsed() {
+        long downtimeA = 100;
         long downtimeB = downtimeA + 100;
 
         MotionEvent evDownA = MotionEvent.obtain(downtimeA,
@@ -206,17 +195,17 @@
                                                downtimeB + 1,
                                                MotionEvent.ACTION_UP,
                                                1,
-                                               1 + mDoubleTouchSlop,
+                                               1,
                                                0);
 
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
-        verify(mActivationListener).onActiveChanged(true);
-        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        mFalsingManager.setFalseDoubleTap(true);
 
-        mDoubleTapHelper.onTouchEvent(evDownB);
-        mDoubleTapHelper.onTouchEvent(evUpB);
-        verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+
+        mNotificationTapHelper.onTouchEvent(evDownB);
+        mNotificationTapHelper.onTouchEvent(evUpB);
         verify(mActivationListener).onActiveChanged(false);
         verify(mDoubleTapListener, never()).onDoubleTap();
 
@@ -228,8 +217,7 @@
 
     @Test
     public void testSlideBack() {
-        long downtimeA = SystemClock.uptimeMillis();
-        long downtimeB = downtimeA + 100;
+        long downtimeA = 100;
 
         MotionEvent evDownA = MotionEvent.obtain(downtimeA,
                                                  downtimeA,
@@ -243,42 +231,24 @@
                                                1,
                                                1,
                                                0);
-        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
-                                                 downtimeB,
-                                                 MotionEvent.ACTION_DOWN,
-                                                 1,
-                                                 1,
-                                                 0);
-        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
-                                               downtimeB + 1,
-                                               MotionEvent.ACTION_UP,
-                                               1,
-                                               1,
-                                               0);
 
         when(mSlideBackListener.onSlideBack()).thenReturn(true);
 
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener, never()).onActiveChanged(true);
         verify(mActivationListener, never()).onActiveChanged(false);
         verify(mDoubleTapListener, never()).onDoubleTap();
-        mDoubleTapHelper.onTouchEvent(evDownB);
-        mDoubleTapHelper.onTouchEvent(evUpB);
-        verify(mActivationListener, never()).onActiveChanged(true);
-        verify(mActivationListener, never()).onActiveChanged(false);
-        verify(mDoubleTapListener, never()).onDoubleTap();
+        verify(mSlideBackListener).onSlideBack();
 
         evDownA.recycle();
         evUpA.recycle();
-        evDownB.recycle();
-        evUpB.recycle();
     }
 
 
     @Test
     public void testMoreThanTwoTaps() {
-        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeA = 100;
         long downtimeB = downtimeA + 100;
         long downtimeC = downtimeB + 100;
         long downtimeD = downtimeC + 100;
@@ -332,33 +302,26 @@
                                                1,
                                                0);
 
-        mDoubleTapHelper.onTouchEvent(evDownA);
-        mDoubleTapHelper.onTouchEvent(evUpA);
+        mNotificationTapHelper.onTouchEvent(evDownA);
+        mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener).onActiveChanged(true);
-        verify(mView).postDelayed(any(Runnable.class), anyLong());
-        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
         verify(mDoubleTapListener, never()).onDoubleTap();
 
-        mDoubleTapHelper.onTouchEvent(evDownB);
-        mDoubleTapHelper.onTouchEvent(evUpB);
-        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        mNotificationTapHelper.onTouchEvent(evDownB);
+        mNotificationTapHelper.onTouchEvent(evUpB);
         verify(mDoubleTapListener).onDoubleTap();
 
         reset(mView);
         reset(mActivationListener);
-        reset(mDoubleTapLogListener);
         reset(mDoubleTapListener);
 
-        mDoubleTapHelper.onTouchEvent(evDownC);
-        mDoubleTapHelper.onTouchEvent(evUpC);
+        mNotificationTapHelper.onTouchEvent(evDownC);
+        mNotificationTapHelper.onTouchEvent(evUpC);
         verify(mActivationListener).onActiveChanged(true);
-        verify(mView).postDelayed(any(Runnable.class), anyLong());
-        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
         verify(mDoubleTapListener, never()).onDoubleTap();
 
-        mDoubleTapHelper.onTouchEvent(evDownD);
-        mDoubleTapHelper.onTouchEvent(evUpD);
-        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        mNotificationTapHelper.onTouchEvent(evDownD);
+        mNotificationTapHelper.onTouchEvent(evUpD);
         verify(mDoubleTapListener).onDoubleTap();
 
         evDownA.recycle();
@@ -366,4 +329,9 @@
         evDownB.recycle();
         evUpB.recycle();
     }
+
+    private void drainExecutor() {
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 9832d31..83030ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -33,6 +33,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
+import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
@@ -103,6 +104,7 @@
         when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class));
         when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class,
                 RETURNS_DEEP_STUBS));
+        when(mLockIconContainer.getLayoutParams()).thenReturn(mock(FrameLayout.LayoutParams.class));
 
         when(mKeyguardBouncerFactory.create(
                 any(ViewGroup.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index ecfb357..d8271e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -28,17 +28,16 @@
  * backwards. uptimeMillis, elapsedRealtime, and currentThreadTimeMillis are all kept in sync.
  *
  * Unless otherwise specified, uptimeMillis and elapsedRealtime will advance the same amount with
- * every call to {@link #advanceTime(long)}. Thread time always lags by 50% of the uptime
+ * every call to {@link #advanceTime}. Thread time always lags by 50% of the uptime
  * advancement to simulate time loss due to scheduling.
  */
 public class FakeSystemClock implements SystemClock {
     private long mUptimeMillis = 10000;
     private long mElapsedRealtime = 10000;
     private long mCurrentThreadTimeMillis = 10000;
-
     private long mCurrentTimeMillis = 1555555500000L;
-
     private final List<ClockTickListener> mListeners = new ArrayList<>();
+
     @Override
     public long uptimeMillis() {
         return mUptimeMillis;
@@ -72,21 +71,38 @@
         mCurrentTimeMillis = millis;
     }
 
-    public void advanceTime(long uptime) {
-        advanceTime(uptime, 0);
+    /**
+     * Advances the time tracked by the fake clock and notifies any listeners that the time has
+     * changed (for example, an attached {@link FakeExecutor} may fire its pending runnables).
+     *
+     * All tracked times increment by [millis], with the exception of currentThreadTimeMillis,
+     * which advances by [millis] * 0.5
+     */
+    public void advanceTime(long millis) {
+        advanceTime(millis, 0);
     }
 
-    public void advanceTime(long uptime, long sleepTime) {
-        if (uptime < 0 || sleepTime < 0) {
+    /**
+     * Advances the time tracked by the fake clock and notifies any listeners that the time has
+     * changed (for example, an attached {@link FakeExecutor} may fire its pending runnables).
+     *
+     * The tracked times change as follows:
+     * - uptimeMillis increments by [awakeMillis]
+     * - currentThreadTimeMillis increments by [awakeMillis] * 0.5
+     * - elapsedRealtime increments by [awakeMillis] + [sleepMillis]
+     * - currentTimeMillis increments by [awakeMillis] + [sleepMillis]
+     */
+    public void advanceTime(long awakeMillis, long sleepMillis) {
+        if (awakeMillis < 0 || sleepMillis < 0) {
             throw new IllegalArgumentException("Time cannot go backwards");
         }
 
-        if (uptime > 0 || sleepTime > 0) {
-            mUptimeMillis += uptime;
-            mElapsedRealtime += uptime + sleepTime;
-            mCurrentTimeMillis += uptime + sleepTime;
+        if (awakeMillis > 0 || sleepMillis > 0) {
+            mUptimeMillis += awakeMillis;
+            mElapsedRealtime += awakeMillis + sleepMillis;
+            mCurrentTimeMillis += awakeMillis + sleepMillis;
 
-            mCurrentThreadTimeMillis += Math.ceil(uptime * 0.5);
+            mCurrentThreadTimeMillis += Math.ceil(awakeMillis * 0.5);
 
             for (ClockTickListener listener : mListeners) {
                 listener.onClockTick();
@@ -105,6 +121,4 @@
     public interface ClockTickListener {
         void onClockTick();
     }
-
-    private static final long START_TIME = 10000;
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 15bd4dc..ae024ff 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -332,5 +332,9 @@
     // Notify the user that the admin suspended personal apps on the device.
     // Package: android
     NOTE_PERSONAL_APPS_SUSPENDED = 1003;
+
+    // Notify the user that window magnification is available.
+    // package: android
+    NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004;
   }
 }
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 2006fb3..4b0e4c2 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -29,7 +29,7 @@
      int32 inset_bottom = 5;
      bool is_real_snapshot = 6;
      int32 windowing_mode = 7;
-     int32 system_ui_visibility = 8;
+     int32 system_ui_visibility = 8 [deprecated=true];
      bool is_translucent = 9;
      string top_activity_component = 10;
      // deprecated because original width and height are stored now instead of the scale.
@@ -40,4 +40,5 @@
      int32 task_width = 14;
      // The task height when the snapshot was taken
      int32 task_height = 15;
+     int32 appearance = 16;
  }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 857ac6a..be7643e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -37,6 +37,7 @@
 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
 import com.android.server.accessibility.magnification.MagnificationGestureHandler;
 import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
+import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.ArrayList;
@@ -561,8 +562,9 @@
                     detectControlGestures, triggerable, displayId);
         } else {
             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
-                    mAms.getFullScreenMagnificationController(), mAms::onMagnificationScaleChanged,
-                    detectControlGestures, triggerable, displayId);
+                    mAms.getFullScreenMagnificationController(),
+                    mAms::onMagnificationScaleChanged, detectControlGestures, triggerable,
+                    new WindowMagnificationPromptController(displayContext, mUserId), displayId);
         }
         return magnificationGestureHandler;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 35481a2..4c85490 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -80,7 +80,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.SettingStringHelper;
 import android.text.TextUtils;
@@ -122,6 +121,7 @@
 import com.android.server.accessibility.magnification.MagnificationController;
 import com.android.server.accessibility.magnification.MagnificationGestureHandler;
 import com.android.server.accessibility.magnification.WindowMagnificationManager;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index 5b46cb4..f731c44 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -18,11 +18,7 @@
 
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
-import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
 import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
-import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
-import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS;
-import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_START_SWIPE_MS;
 import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
 
 import android.content.Context;
@@ -30,7 +26,6 @@
 import android.os.Handler;
 import android.util.DisplayMetrics;
 import android.util.Slog;
-import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
@@ -49,9 +44,6 @@
     public static final int RIGHT = 1;
     public static final int UP = 2;
     public static final int DOWN = 3;
-    // This is the calculated movement threshold used track if the user is still
-    // moving their finger.
-    private final float mGestureDetectionThresholdPixels;
 
     // Buffer for storing points for gesture detection.
     private final ArrayList<PointF>[] mStrokeBuffers;
@@ -93,10 +85,7 @@
         mStrokeBuffers = new ArrayList[mTargetFingerCount];
         mDirection = direction;
         DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
-        mGestureDetectionThresholdPixels =
-                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
-                        * GESTURE_CONFIRM_CM;
-        // Calculate minimum gesture velocity
+        // Calculate gesture sampling interval.
         final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH;
         final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH;
         mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
@@ -150,7 +139,6 @@
             return;
         }
         mPointerIds[pointerIndex] = pointerId;
-        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
         if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
             final float x = rawEvent.getX(actionIndex);
             final float y = rawEvent.getY(actionIndex);
@@ -197,7 +185,6 @@
             return;
         }
         mPointerIds[pointerIndex] = pointerId;
-        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
         if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
             final float x = rawEvent.getX(actionIndex);
             final float y = rawEvent.getY(actionIndex);
@@ -286,65 +273,42 @@
                             Math.abs(x - mBase[pointerIndex].x),
                             Math.abs(y - mBase[pointerIndex].y));
             if (DEBUG) {
-                Slog.d(
-                        getGestureName(),
-                        "moveDelta:"
-                                + Double.toString(moveDelta)
-                                + " mGestureDetectionThreshold: "
-                                + Float.toString(mGestureDetectionThresholdPixels));
+                Slog.d(getGestureName(), "moveDelta:" + moveDelta);
             }
             if (getState() == STATE_CLEAR) {
                 if (moveDelta < (mTargetFingerCount * mTouchSlop)) {
                     // This still counts as a touch not a swipe.
                     continue;
-                } else if (mStrokeBuffers[pointerIndex].size() == 0) {
-                    // First, make sure we have the right number of fingers down.
-                    if (mCurrentFingerCount != mTargetFingerCount) {
-                        cancelGesture(event, rawEvent, policyFlags);
-                        return;
-                    }
-                    // Then, make sure the pointer is going in the right direction.
-                    int direction =
-                            toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
-                    if (direction != mDirection) {
-                        cancelGesture(event, rawEvent, policyFlags);
-                        return;
-                    } else {
-                        // This is confirmed to be some kind of swipe so start tracking points.
-                        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
-                        for (int i = 0; i < mTargetFingerCount; ++i) {
-                            mStrokeBuffers[i].add(new PointF(mBase[i]));
-                        }
-                    }
                 }
-                if (moveDelta > mGestureDetectionThresholdPixels) {
-                    // Try to cancel if the finger starts to go the wrong way.
-                    // Note that this only works because this matcher assumes one direction.
-                    int direction =
-                            toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
-                    if (direction != mDirection) {
-                        cancelGesture(event, rawEvent, policyFlags);
-                        return;
-                    }
-                    // If the pointer has moved more than the threshold,
-                    // update the stored values.
-                    mBase[pointerIndex].x = x;
-                    mBase[pointerIndex].y = y;
-                    mPreviousGesturePoint[pointerIndex].x = x;
-                    mPreviousGesturePoint[pointerIndex].y = y;
-                    if (getState() == STATE_CLEAR) {
-                        startGesture(event, rawEvent, policyFlags);
-                        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
-                    }
+                // First, make sure we have the right number of fingers down.
+                if (mCurrentFingerCount != mTargetFingerCount) {
+                    cancelGesture(event, rawEvent, policyFlags);
+                    return;
                 }
-            }
-            if (getState() == STATE_GESTURE_STARTED) {
+                // Then, make sure the pointer is going in the right direction.
+                int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+                if (direction != mDirection) {
+                    cancelGesture(event, rawEvent, policyFlags);
+                    return;
+                }
+                // This is confirmed to be some kind of swipe so start tracking points.
+                startGesture(event, rawEvent, policyFlags);
+                for (int i = 0; i < mTargetFingerCount; ++i) {
+                    mStrokeBuffers[i].add(new PointF(mBase[i]));
+                }
+            } else if (getState() == STATE_GESTURE_STARTED) {
+                // Cancel if the finger starts to go the wrong way.
+                // Note that this only works because this matcher assumes one direction.
+                int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+                if (direction != mDirection) {
+                    cancelGesture(event, rawEvent, policyFlags);
+                    return;
+                }
                 if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
                     // Sample every 2.5 MM in order to guard against minor variations in path.
                     mPreviousGesturePoint[pointerIndex].x = x;
                     mPreviousGesturePoint[pointerIndex].y = y;
                     mStrokeBuffers[pointerIndex].add(new PointF(x, y));
-                    cancelAfterPauseThreshold(event, rawEvent, policyFlags);
                 }
             }
         }
@@ -379,24 +343,6 @@
     }
 
     /**
-     * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
-     * transitioned to STATE_GESTURE_STARTED the delay is longer.
-     */
-    private void cancelAfterPauseThreshold(
-            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        cancelPendingTransitions();
-        switch (getState()) {
-            case STATE_CLEAR:
-                cancelAfter(MAX_TIME_TO_START_SWIPE_MS, event, rawEvent, policyFlags);
-                break;
-            case STATE_GESTURE_STARTED:
-                cancelAfter(MAX_TIME_TO_CONTINUE_SWIPE_MS, event, rawEvent, policyFlags);
-                break;
-            default:
-                break;
-        }
-    }
-    /**
      * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions
      * to the complete or cancel state depending on the result.
      */
@@ -502,8 +448,6 @@
         if (getState() != STATE_GESTURE_CANCELED) {
             builder.append(", mBase: ")
                     .append(mBase.toString())
-                    .append(", mGestureDetectionThreshold:")
-                    .append(mGestureDetectionThresholdPixels)
                     .append(", mMinPixelsBetweenSamplesX:")
                     .append(mMinPixelsBetweenSamplesX)
                     .append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d50e9d7..efb9d87 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@
     @VisibleForTesting final ViewportDraggingState mViewportDraggingState;
 
     private final ScreenStateReceiver mScreenStateReceiver;
+    private final WindowMagnificationPromptController mPromptController;
 
     /**
      * {@code true} if this detector should detect and respond to triple-tap
@@ -178,6 +179,7 @@
             ScaleChangedListener listener,
             boolean detectTripleTap,
             boolean detectShortcutTrigger,
+            @NonNull WindowMagnificationPromptController promptController,
             int displayId) {
         super(listener);
         if (DEBUG_ALL) {
@@ -186,6 +188,7 @@
                             + ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
         }
         mFullScreenMagnificationController = fullScreenMagnificationController;
+        mPromptController = promptController;
         mDisplayId = displayId;
 
         mDelegatingState = new DelegatingState();
@@ -195,7 +198,6 @@
 
         mDetectTripleTap = detectTripleTap;
         mDetectShortcutTrigger = detectShortcutTrigger;
-
         if (mDetectShortcutTrigger) {
             mScreenStateReceiver = new ScreenStateReceiver(context, this);
             mScreenStateReceiver.register();
@@ -264,6 +266,7 @@
         if (mScreenStateReceiver != null) {
             mScreenStateReceiver.unregister();
         }
+        mPromptController.onDestroy();
         // Check if need to reset when MagnificationGestureHandler is the last magnifying service.
         mFullScreenMagnificationController.resetAllIfNeeded(
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -278,6 +281,7 @@
             if (wasMagnifying) {
                 clearAndTransitionToStateDetecting();
             } else {
+                mPromptController.showNotificationIfNeeded();
                 mDetectingState.toggleShortcutTriggered();
             }
         }
@@ -950,6 +954,7 @@
             if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
                 zoomOff();
             } else {
+                mPromptController.showNotificationIfNeeded();
                 zoomOn(up.getX(), up.getY());
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
new file mode 100644
index 0000000..7212207
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -0,0 +1,211 @@
+/*
+ * 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.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE;
+
+import android.Manifest;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.app.ActivityOptions;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.notification.SystemNotificationChannels;
+
+/**
+ * A class to show notification to prompt the user that this feature is available.
+ */
+public class WindowMagnificationPromptController {
+
+    private static final Uri MAGNIFICATION_WINDOW_MODE_PROMPT_URI = Settings.Secure.getUriFor(
+            ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT);
+    @VisibleForTesting
+    static final String ACTION_DISMISS =
+            "com.android.server.accessibility.magnification.action.DISMISS";
+    @VisibleForTesting
+    static final String ACTION_TURN_ON_IN_SETTINGS =
+            "com.android.server.accessibility.magnification.action.TURN_ON_IN_SETTINGS";
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+    private final ContentObserver mContentObserver;
+    private final int mUserId;
+    @VisibleForTesting
+    BroadcastReceiver mNotificationActionReceiver;
+
+    private boolean mNeedToShowNotification;
+
+    @MainThread
+    public WindowMagnificationPromptController(@NonNull Context context, int userId) {
+        mContext = context;
+        mNotificationManager = context.getSystemService(NotificationManager.class);
+        mUserId = userId;
+        mContentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                super.onChange(selfChange);
+                onPromptSettingsValueChanged();
+            }
+        };
+        context.getContentResolver().registerContentObserver(MAGNIFICATION_WINDOW_MODE_PROMPT_URI,
+                false, mContentObserver, mUserId);
+        mNeedToShowNotification = isWindowMagnificationPromptEnabled();
+    }
+
+    @VisibleForTesting
+    protected void onPromptSettingsValueChanged() {
+        final boolean needToShowNotification = isWindowMagnificationPromptEnabled();
+        if (mNeedToShowNotification == needToShowNotification) {
+            return;
+        }
+        mNeedToShowNotification = needToShowNotification;
+        if (!mNeedToShowNotification) {
+            unregisterReceiverIfNeeded();
+            mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+        }
+    }
+
+    /**
+     * Shows the prompt notification that could bring users to magnification settings if necessary.
+     */
+    @MainThread
+    void showNotificationIfNeeded() {
+        if (!mNeedToShowNotification) return;
+
+        final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+                SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION);
+        notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp)
+                .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title))
+                .setContentText(mContext.getString(R.string.window_magnification_prompt_content))
+                .setLargeIcon(Icon.createWithResource(mContext,
+                        R.drawable.ic_accessibility_magnification))
+                .setTicker(mContext.getString(R.string.window_magnification_prompt_title))
+                .setOnlyAlertOnce(true)
+                .setDeleteIntent(createPendingIntent(ACTION_DISMISS))
+                .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS))
+                .setActions(buildTurnOnAction(), buildDismissAction());
+        mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE,
+                notificationBuilder.build());
+        registerReceiverIfNeeded();
+    }
+
+    /**
+     * Called when this object is not used anymore to release resources if necessary.
+     */
+    @VisibleForTesting
+    @MainThread
+    public void onDestroy() {
+        dismissNotification();
+        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+    }
+
+    private boolean isWindowMagnificationPromptEnabled() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId) == 1;
+    }
+
+    private Notification.Action buildTurnOnAction() {
+        return new Notification.Action.Builder(null,
+                mContext.getString(R.string.turn_on_magnification_settings_action),
+                createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build();
+    }
+
+    private Notification.Action buildDismissAction() {
+        return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action),
+                createPendingIntent(ACTION_DISMISS)).build();
+    }
+
+    private PendingIntent createPendingIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.setPackage(mContext.getPackageName());
+        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    private void registerReceiverIfNeeded() {
+        if (mNotificationActionReceiver != null) {
+            return;
+        }
+        mNotificationActionReceiver = new NotificationActionReceiver();
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_DISMISS);
+        intentFilter.addAction(ACTION_TURN_ON_IN_SETTINGS);
+        mContext.registerReceiver(mNotificationActionReceiver, intentFilter,
+                Manifest.permission.MANAGE_ACCESSIBILITY, null);
+    }
+
+    private void launchMagnificationSettings() {
+        final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+                MAGNIFICATION_COMPONENT_NAME.flattenToShortString());
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
+                mContext.getDisplayId()).toBundle();
+        mContext.startActivityAsUser(intent, bundle, UserHandle.of(mUserId));
+        mContext.getSystemService(StatusBarManager.class).collapsePanels();
+    }
+
+    private void dismissNotification() {
+        unregisterReceiverIfNeeded();
+        mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+    }
+
+    private void unregisterReceiverIfNeeded() {
+        if (mNotificationActionReceiver == null) {
+            return;
+        }
+        mContext.unregisterReceiver(mNotificationActionReceiver);
+        mNotificationActionReceiver = null;
+    }
+
+    private class NotificationActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (TextUtils.isEmpty(action)) return;
+
+            mNeedToShowNotification = false;
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId);
+
+            if (ACTION_TURN_ON_IN_SETTINGS.equals(action)) {
+                launchMagnificationSettings();
+                dismissNotification();
+            } else if (ACTION_DISMISS.equals(action)) {
+                dismissNotification();
+            }
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index e323249..5fedf9f 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -62,7 +62,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
@@ -88,6 +87,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4bde31f..069a5ea 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -61,6 +61,7 @@
 
 java_library_static {
     name: "services.core.unboosted",
+    defaults: ["platform_service_defaults"],
     srcs: [
         ":services.core.protologsrc",
         ":dumpstate_aidl",
@@ -74,6 +75,7 @@
         ":platform-compat-config",
         ":display-device-config",
         ":cec-config",
+        ":device-state-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
@@ -146,7 +148,6 @@
 
 java_library {
     name: "services.core",
-    defaults: ["platform_service_defaults"],
     static_libs: ["services.core.priorityboosted"],
 }
 
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 9455051a..bb4bbd5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -437,6 +437,10 @@
                 info.legacy.legacy.batteryChargeCounter);
         Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
                 info.legacy.legacy.batteryCurrent);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
+                plugType(info.legacy.legacy));
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
+                info.legacy.legacy.batteryStatus);
 
         synchronized (mLock) {
             if (!mUpdatesStopped) {
@@ -471,6 +475,18 @@
         dst.batteryTechnology = src.batteryTechnology;
     }
 
+    private static int plugType(HealthInfo healthInfo) {
+        if (healthInfo.chargerAcOnline) {
+            return BatteryManager.BATTERY_PLUGGED_AC;
+        } else if (healthInfo.chargerUsbOnline) {
+            return BatteryManager.BATTERY_PLUGGED_USB;
+        } else if (healthInfo.chargerWirelessOnline) {
+            return BatteryManager.BATTERY_PLUGGED_WIRELESS;
+        } else {
+            return BATTERY_PLUGGED_NONE;
+        }
+    }
+
     private void processValuesLocked(boolean force) {
         boolean logOutlier = false;
         long dischargeDuration = 0;
@@ -478,15 +494,7 @@
         mBatteryLevelCritical =
             mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
             && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
-        if (mHealthInfo.chargerAcOnline) {
-            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
-        } else if (mHealthInfo.chargerUsbOnline) {
-            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
-        } else if (mHealthInfo.chargerWirelessOnline) {
-            mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
-        } else {
-            mPlugType = BATTERY_PLUGGED_NONE;
-        }
+        mPlugType = plugType(mHealthInfo);
 
         if (DEBUG) {
             Slog.d(TAG, "Processing new values: "
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 66ac889..fdda239 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -131,6 +131,7 @@
         private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
         private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
         private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
+        private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
 
         private boolean mEnabled;
         private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -184,6 +185,9 @@
             mBinderCallsStats.setTrackDirectCallerUid(
                     mParser.getBoolean(SETTINGS_TRACK_DIRECT_CALLING_UID_KEY,
                     BinderCallsStats.DEFAULT_TRACK_DIRECT_CALLING_UID));
+            mBinderCallsStats.setIgnoreBatteryStatus(
+                    mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+                    BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS));
 
 
             final boolean enabled =
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f6a29aa..0a68428 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -63,8 +63,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
@@ -77,6 +75,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.pm.UserRestrictionsUtils;
 
 import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d586f00..2fe37d4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -137,7 +137,6 @@
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
-import android.os.BasicShellCommandHandler;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -190,6 +189,7 @@
 import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.server.am.BatteryStatsService;
@@ -2697,10 +2697,16 @@
     /**
      * Return an array of all current NetworkRequest sorted by request id.
      */
-    private NetworkRequestInfo[] requestsSortedById() {
+    @VisibleForTesting
+    protected NetworkRequestInfo[] requestsSortedById() {
         NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
         requests = mNetworkRequests.values().toArray(requests);
-        Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId));
+        // Sort the array based off the NRI containing the min requestId in its requests.
+        Arrays.sort(requests,
+                Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
+                        Comparator.comparingInt(req -> req.requestId)).requestId
+                )
+        );
         return requests;
     }
 
@@ -5350,6 +5356,12 @@
         }
     }
 
+    private void ensureAllNetworkRequestsHaveType(List<NetworkRequest> requests) {
+        for (int i = 0; i < requests.size(); i++) {
+            ensureNetworkRequestHasType(requests.get(i));
+        }
+    }
+
     private void ensureNetworkRequestHasType(NetworkRequest request) {
         if (request.type == NetworkRequest.Type.NONE) {
             throw new IllegalArgumentException(
@@ -5361,7 +5373,8 @@
      * Tracks info about the requester.
      * Also used to notice when the calling process dies so we can self-expire
      */
-    private class NetworkRequestInfo implements IBinder.DeathRecipient {
+    @VisibleForTesting
+    protected class NetworkRequestInfo implements IBinder.DeathRecipient {
         final List<NetworkRequest> mRequests;
         final NetworkRequest request;
 
@@ -5380,7 +5393,7 @@
         NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
             request = r;
             mRequests = initializeRequests(r);
-            ensureNetworkRequestHasType(request);
+            ensureAllNetworkRequestsHaveType(mRequests);
             mPendingIntent = pi;
             messenger = null;
             mBinder = null;
@@ -5394,7 +5407,7 @@
             messenger = m;
             request = r;
             mRequests = initializeRequests(r);
-            ensureNetworkRequestHasType(request);
+            ensureAllNetworkRequestsHaveType(mRequests);
             mBinder = binder;
             mPid = getCallingPid();
             mUid = getCallingUid();
@@ -5418,6 +5431,19 @@
             return Collections.unmodifiableList(tempRequests);
         }
 
+        private NetworkRequest getSatisfiedRequest() {
+            if (mSatisfier == null) {
+                return null;
+            }
+
+            for (NetworkRequest req : mRequests) {
+                if (mSatisfier.isSatisfyingRequest(req.requestId)) {
+                    return req;
+                }
+            }
+
+            return null;
+        }
 
         private void enforceRequestCountLimit() {
             synchronized (mUidToNetworkRequestCount) {
@@ -5436,14 +5462,16 @@
             }
         }
 
+        @Override
         public void binderDied() {
             log("ConnectivityService NetworkRequestInfo binderDied(" +
-                    request + ", " + mBinder + ")");
-            releaseNetworkRequest(request);
+                    mRequests + ", " + mBinder + ")");
+            releaseNetworkRequest(mRequests);
         }
 
+        @Override
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " " + request
+            return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
     }
@@ -5763,6 +5791,12 @@
         return mNextNetworkProviderId.getAndIncrement();
     }
 
+    private void releaseNetworkRequest(List<NetworkRequest> networkRequests) {
+        for (int i = 0; i < networkRequests.size(); i++) {
+            releaseNetworkRequest(networkRequests.get(i));
+        }
+    }
+
     @Override
     public void releaseNetworkRequest(NetworkRequest networkRequest) {
         ensureNetworkRequestHasType(networkRequest);
@@ -5854,7 +5888,6 @@
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
-    // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
     @NonNull
     private final NetworkRequest mDefaultRequest;
     // The NetworkAgentInfo currently satisfying the default request, if any.
@@ -6780,7 +6813,7 @@
             }
 
             public String toString() {
-                return mRequest.request.requestId + " : "
+                return mRequest.mRequests.get(0).requestId + " : "
                         + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
                         + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
             }
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index d4e912b6..8ed23f9 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -44,6 +44,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -145,16 +148,44 @@
     private long mLastPowerDown;
     private int mPowerButtonConsecutiveTaps;
     private int mPowerButtonSlowConsecutiveTaps;
+    private final UiEventLogger mUiEventLogger;
 
+    @VisibleForTesting
+    public enum GestureLauncherEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The user lifted the device just the right way to launch the camera.")
+        GESTURE_CAMERA_LIFT(658),
+
+        @UiEvent(doc = "The user wiggled the device just the right way to launch the camera.")
+        GESTURE_CAMERA_WIGGLE(659),
+
+        @UiEvent(doc = "The user double-tapped power quickly enough to launch the camera.")
+        GESTURE_CAMERA_DOUBLE_TAP_POWER(660),
+
+        @UiEvent(doc = "The user multi-tapped power quickly enough to signal an emergency.")
+        GESTURE_PANIC_TAP_POWER(661);
+
+        private final int mId;
+
+        GestureLauncherEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
     public GestureLauncherService(Context context) {
-        this(context, new MetricsLogger());
+        this(context, new MetricsLogger(), new UiEventLoggerImpl());
     }
 
     @VisibleForTesting
-    GestureLauncherService(Context context, MetricsLogger metricsLogger) {
+    GestureLauncherService(Context context, MetricsLogger metricsLogger,
+            UiEventLogger uiEventLogger) {
         super(context);
         mContext = context;
         mMetricsLogger = metricsLogger;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -460,11 +491,12 @@
             if (launchCamera) {
                 mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
                         (int) powerTapInterval);
+                mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
             }
         } else if (launchEmergencyGesture) {
             Slog.i(TAG, "Emergency gesture detected, launching.");
             launchEmergencyGesture = handleEmergencyGesture();
-            // TODO(b/160006048): Add logging
+            mUiEventLogger.log(GestureLauncherEvent.GESTURE_PANIC_TAP_POWER);
         }
         mMetricsLogger.histogram("power_consecutive_short_tap_count",
                 mPowerButtonSlowConsecutiveTaps);
@@ -587,6 +619,7 @@
                 if (handleCameraGesture(true /* useWakelock */,
                         StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
                     mMetricsLogger.action(MetricsEvent.ACTION_WIGGLE_CAMERA_GESTURE);
+                    mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_WIGGLE);
                     trackCameraLaunchEvent(event);
                 }
                 return;
@@ -671,6 +704,7 @@
                     if (handleCameraGesture(true /* useWakelock */,
                             StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) {
                         MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER);
+                        mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_LIFT);
                     }
                 } else {
                     if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event");
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 965b64b..de40b52 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -52,6 +52,7 @@
     private static final String SETTINGS_ENABLED_KEY = "enabled";
     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
     private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
+    private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
     private static final String DEBUG_SYS_LOOPER_STATS_ENABLED =
             "debug.sys.looper_stats_enabled";
     private static final int DEFAULT_SAMPLING_INTERVAL = 1000;
@@ -64,6 +65,7 @@
     // Default should be false so that the first call to #setEnabled installed the looper observer.
     private boolean mEnabled = false;
     private boolean mTrackScreenInteractive = false;
+    private boolean mIgnoreBatteryStatus = LooperStats.DEFAULT_IGNORE_BATTERY_STATUS;
 
     private LooperStatsService(Context context, LooperStats stats) {
         this.mContext = context;
@@ -85,6 +87,9 @@
         setTrackScreenInteractive(
                 parser.getBoolean(SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY,
                 DEFAULT_TRACK_SCREEN_INTERACTIVE));
+        setIgnoreBatteryStatus(
+                parser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+                LooperStats.DEFAULT_IGNORE_BATTERY_STATUS));
         // Manually specified value takes precedence over Settings.
         setEnabled(SystemProperties.getBoolean(
                 DEBUG_SYS_LOOPER_STATS_ENABLED,
@@ -168,6 +173,14 @@
         }
     }
 
+    private void setIgnoreBatteryStatus(boolean ignore) {
+        if (mIgnoreBatteryStatus != ignore) {
+            mStats.setIgnoreBatteryStatus(ignore);
+            mIgnoreBatteryStatus = ignore;
+            mStats.reset();
+        }
+    }
+
     private void setSamplingInterval(int samplingInterval) {
         if (samplingInterval > 0) {
             mStats.setSamplingInterval(samplingInterval);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 783866a..8033ce7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -102,7 +102,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.DiskInfo;
 import android.os.storage.IObbActionListener;
 import android.os.storage.IStorageEventListener;
@@ -151,6 +150,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.pm.Installer;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
 import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 71a1821..9d60b84 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -21,10 +21,10 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.os.Build;
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.os.UserManagerInternal;
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
@@ -32,9 +32,11 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.ClassLoaderFactory;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService.TargetUser;
 import com.android.server.am.EventLogTags;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import dalvik.system.PathClassLoader;
@@ -121,7 +123,10 @@
         if (pathClassLoader == null) {
             // NB: the parent class loader should always be the system server class loader.
             // Changing it has implications that require discussion with the mainline team.
-            pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader());
+            pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader(
+                    path, null /* librarySearchPath */, null /* libraryPermittedPath */,
+                    this.getClass().getClassLoader(), Build.VERSION.SDK_INT,
+                    true /* isNamespaceShared */, null /* classLoaderName */);
             mLoadedPaths.put(path, pathClassLoader);
         }
         final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ba52aee..eb55512 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -80,7 +80,6 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Pair;
 
@@ -102,13 +101,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * Since phone process can be restarted, this class provides a centralized place
@@ -148,14 +144,14 @@
         int callerUid;
         int callerPid;
 
-        Set<Integer> eventList;
+        long events;
 
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
         int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 
-        boolean matchPhoneStateListenerEvent(int event) {
-            return (callback != null) && (this.eventList.contains(event));
+        boolean matchPhoneStateListenerEvent(long events) {
+            return (callback != null) && ((events & this.events) != 0);
         }
 
         boolean matchOnSubscriptionsChangedListener() {
@@ -183,7 +179,7 @@
                     + onSubscriptionsChangedListenerCallback
                     + " onOpportunisticSubscriptionsChangedListenererCallback="
                     + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
-                    + " phoneId=" + phoneId + " events=" + eventList + "}";
+                    + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}";
         }
     }
 
@@ -314,10 +310,6 @@
 
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
 
-    private boolean mIsDataEnabled = false;
-
-    private int mDataEnabledReason;
-
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
      * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -327,62 +319,40 @@
     private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
             mPreciseDataConnectionStates;
 
-    private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION;
-    static {
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>();
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
-    }
+    // Starting in Q, almost all cellular location requires FINE location enforcement.
+    // Prior to Q, cellular was available with COARSE location enforcement. Bits in this
+    // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later.
+    static final long ENFORCE_LOCATION_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_CELL_LOCATION
+                    | PhoneStateListener.LISTEN_CELL_INFO
+                    | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                    | PhoneStateListener.LISTEN_BARRING_INFO;
 
-    private boolean isLocationPermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE)
-                || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
-    }
+    static final long ENFORCE_PHONE_STATE_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+                    | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+                    | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+                    | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
 
-    private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
-    }
+    static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                    | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+                    | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+                    | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+                    | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
+                    | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                    | PhoneStateListener.LISTEN_BARRING_INFO
+                    | PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION;
 
-    private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
-        for (Integer requireEvent : REQUIRE_PRECISE_PHONE_STATE_PERMISSION) {
-            if (events.contains(requireEvent)) {
-                return true;
-            }
-        }
-        return false;
-    }
+    static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
+                    | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
 
-    private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)
-                || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
-    }
-
-    private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
-    }
+    static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT
+                    | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
+                    | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
+                    | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
 
     private static final int MSG_USER_SWITCHED = 1;
     private static final int MSG_UPDATE_DEFAULT_SUB = 2;
@@ -700,7 +670,7 @@
             r.callingFeatureId = callingFeatureId;
             r.callerUid = Binder.getCallingUid();
             r.callerPid = Binder.getCallingPid();
-            r.eventList = new ArraySet<>();
+            r.events = 0;
             if (DBG) {
                 log("listen oscl:  Register r=" + r);
             }
@@ -754,7 +724,7 @@
             r.callingFeatureId = callingFeatureId;
             r.callerUid = Binder.getCallingUid();
             r.callerPid = Binder.getCallingPid();
-            r.eventList = new ArraySet<>();
+            r.events = 0;
             if (DBG) {
                 log("listen ooscl:  Register r=" + r);
             }
@@ -827,338 +797,331 @@
         }
     }
 
+    @Deprecated
     @Override
-    public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
-            IPhoneStateListener callback, int[] events, boolean notifyNow) {
-        Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
-        listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
+    public void listen(String callingPackage, IPhoneStateListener callback, int events,
+            boolean notifyNow) {
+        listenWithFeature(callingPackage, null, callback, events, notifyNow);
+    }
+
+    @Override
+    public void listenWithFeature(String callingPackage, String callingFeatureId,
+            IPhoneStateListener callback, long events, boolean notifyNow) {
+        listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage,
+                callingFeatureId, callback, events, notifyNow);
+    }
+
+    @Override
+    public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId,
+            IPhoneStateListener callback, long events, boolean notifyNow) {
+        listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId);
     }
 
     private void listen(String callingPackage, @Nullable String callingFeatureId,
-            IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {
+            IPhoneStateListener callback, long events, boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
-
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
-                + " events=" + events + " notifyNow=" + notifyNow
-                + " subId=" + subId + " myUserId=" + UserHandle.myUserId()
-                + " callerUserId=" + callerUserId;
+                + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId="
+                + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId;
         mListenLog.log(str);
         if (VDBG) {
             log(str);
         }
 
-        if (events.isEmpty()) {
-            if (DBG) {
-                log("listen: Unregister");
-            }
-            events.clear();
-            remove(callback.asBinder());
-            return;
-        }
-
-        // Checks permission and throws SecurityException for disallowed operations. For pre-M
-        // apps whose runtime permission has been revoked, we return immediately to skip sending
-        // events to the app without crashing it.
-        if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
-                "listen")) {
-            return;
-        }
-
-        int phoneId = getPhoneIdFromSubId(subId);
-        synchronized (mRecords) {
-            // register
-            IBinder b = callback.asBinder();
-            boolean doesLimitApply =
-                    Binder.getCallingUid() != Process.SYSTEM_UID
-                            && Binder.getCallingUid() != Process.PHONE_UID
-                            && Binder.getCallingUid() != Process.myUid();
-            Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
-
-            if (r == null) {
+        if (events != PhoneStateListener.LISTEN_NONE) {
+            // Checks permission and throws SecurityException for disallowed operations. For pre-M
+            // apps whose runtime permission has been revoked, we return immediately to skip sending
+            // events to the app without crashing it.
+            if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
+                    "listen")) {
                 return;
             }
 
-            r.context = mContext;
-            r.callback = callback;
-            r.callingPackage = callingPackage;
-            r.callingFeatureId = callingFeatureId;
-            r.callerUid = Binder.getCallingUid();
-            r.callerPid = Binder.getCallingPid();
-            // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
-            // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
-            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-                r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-            } else {//APP specify subID
-                r.subId = subId;
-            }
-            r.phoneId = phoneId;
-            r.eventList = events;
-            if (DBG) {
-                log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
-            }
-            if (notifyNow && validatePhoneId(phoneId)) {
-                if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) {
-                    try {
-                        if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
-                        ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
-                        if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                            r.callback.onServiceStateChanged(rawSs);
-                        } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                            r.callback.onServiceStateChanged(
-                                    rawSs.createLocationInfoSanitizedCopy(false));
-                        } else {
-                            r.callback.onServiceStateChanged(
-                                    rawSs.createLocationInfoSanitizedCopy(true));
+            int phoneId = getPhoneIdFromSubId(subId);
+            synchronized (mRecords) {
+                // register
+                IBinder b = callback.asBinder();
+                boolean doesLimitApply =
+                        Binder.getCallingUid() != Process.SYSTEM_UID
+                        && Binder.getCallingUid() != Process.PHONE_UID
+                        && Binder.getCallingUid() != Process.myUid();
+                Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
+
+                if (r == null) {
+                    return;
+                }
+
+                r.context = mContext;
+                r.callback = callback;
+                r.callingPackage = callingPackage;
+                r.callingFeatureId = callingFeatureId;
+                r.callerUid = Binder.getCallingUid();
+                r.callerPid = Binder.getCallingPid();
+                // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
+                // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
+                if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                    r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+                 } else {//APP specify subID
+                    r.subId = subId;
+                }
+                r.phoneId = phoneId;
+                r.events = events;
+                if (DBG) {
+                    log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
+                }
+                if (notifyNow && validatePhoneId(phoneId)) {
+                    if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+                        try {
+                            if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
+                            ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(
+                                        rawSs.createLocationInfoSanitizedCopy(false));
+                            } else {
+                                r.callback.onServiceStateChanged(
+                                        rawSs.createLocationInfoSanitizedCopy(true));
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
-                    try {
-                        if (mSignalStrength[phoneId] != null) {
-                            int gsmSignalStrength = mSignalStrength[phoneId]
-                                    .getGsmSignalStrength();
-                            r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
-                                    : gsmSignalStrength));
+                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+                        try {
+                            if (mSignalStrength[phoneId] != null) {
+                                int gsmSignalStrength = mSignalStrength[phoneId]
+                                        .getGsmSignalStrength();
+                                r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+                                        : gsmSignalStrength));
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
-                    try {
-                        r.callback.onMessageWaitingIndicatorChanged(
-                                mMessageWaiting[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
-                    try {
-                        r.callback.onCallForwardingIndicatorChanged(
-                                mCallForwarding[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (validateEventAndUserLocked(
-                        r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
-                    try {
-                        if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
-                        if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
-                                && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                            // null will be translated to empty CellLocation object in client.
-                            r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
+                    if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
+                        try {
+                            r.callback.onMessageWaitingIndicatorChanged(
+                                    mMessageWaiting[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) {
-                    try {
-                        r.callback.onCallStateChanged(mCallState[phoneId],
-                                getCallIncomingNumber(r, phoneId));
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
+                        try {
+                            r.callback.onCallForwardingIndicatorChanged(
+                                    mCallForwarding[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
-                    try {
-                        r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
+                    if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
+                        try {
+                            if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
+                            if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+                                    && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                // null will be translated to empty CellLocation object in client.
+                                r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
+                        try {
+                            r.callback.onCallStateChanged(mCallState[phoneId],
+                                     getCallIncomingNumber(r, phoneId));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+                        try {
+                            r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
                                 mDataConnectionNetworkType[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) {
-                    try {
-                        r.callback.onDataActivity(mDataActivity[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
-                    try {
-                        if (mSignalStrength[phoneId] != null) {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
-                    updateReportSignalStrengthDecision(r.subId);
-                    try {
-                        if (mSignalStrength[phoneId] != null) {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                    if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
+                        try {
+                            r.callback.onDataActivity(mDataActivity[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (validateEventAndUserLocked(
-                        r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
-                    try {
-                        if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
-                                + mCellInfo.get(phoneId));
-                        if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
-                                && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                            r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+                        try {
+                            if (mSignalStrength[phoneId] != null) {
+                                r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) {
-                    try {
-                        r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
-                    try {
-                        r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
-                                mCallPreciseDisconnectCause[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
-                    try {
-                        r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
-                    try {
-                        for (PreciseDataConnectionState pdcs
-                                : mPreciseDataConnectionStates.get(phoneId).values()) {
-                            r.callback.onPreciseDataConnectionStateChanged(pdcs);
+                    if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+                            != 0) {
+                        updateReportSignalStrengthDecision(r.subId);
+                        try {
+                            if (mSignalStrength[phoneId] != null) {
+                                r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) {
-                    try {
-                        r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
-                    try {
-                        r.callback.onVoiceActivationStateChanged(
-                                mVoiceActivationState[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
-                    try {
-                        r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
-                    try {
-                        r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
-                if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
-                    try {
-                        if (mTelephonyDisplayInfos[phoneId] != null) {
-                            r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
+                    if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
+                        try {
+                            if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+                                    + mCellInfo.get(phoneId));
+                            if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+                                    && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
                         }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
-                    try {
-                        r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+                        try {
+                            r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
-                    try {
-                        r.callback.onPhoneCapabilityChanged(mPhoneCapability);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+                        try {
+                            r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
+                                    mCallPreciseDisconnectCause[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
-                    try {
-                        r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
+                        try {
+                            r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) {
-                    try {
-                        r.callback.onRadioPowerStateChanged(mRadioPowerState);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+                        try {
+                            for (PreciseDataConnectionState pdcs
+                                    : mPreciseDataConnectionStates.get(phoneId).values()) {
+                                r.callback.onPreciseDataConnectionStateChanged(pdcs);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) {
-                    try {
-                        r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+                        try {
+                            r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) {
-                    try {
-                        r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) {
+                        try {
+                            r.callback.onVoiceActivationStateChanged(
+                                    mVoiceActivationState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) {
-                    BarringInfo barringInfo = mBarringInfo.get(phoneId);
-                    BarringInfo biNoLocation = barringInfo != null
-                            ? barringInfo.createLocationInfoSanitizedCopy() : null;
-                    if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
-                    try {
-                        r.callback.onBarringInfoChanged(
-                                checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
-                                        ? barringInfo : biNoLocation);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) {
+                        try {
+                            r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
-                    try {
-                        r.callback.onPhysicalChannelConfigChanged(
-                                mPhysicalChannelConfigs);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+                        try {
+                            r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
-                }
-                if (events.contains(
-                        PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
-                    try {
-                        r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+                        try {
+                            if (mTelephonyDisplayInfos[phoneId] != null) {
+                                r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+                        try {
+                            r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
+                        try {
+                            r.callback.onPhoneCapabilityChanged(mPhoneCapability);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener
+                            .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
+                        try {
+                            r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+                        try {
+                            r.callback.onRadioPowerStateChanged(mRadioPowerState);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
+                        try {
+                            r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
+                        try {
+                            r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
+                        BarringInfo barringInfo = mBarringInfo.get(phoneId);
+                        BarringInfo biNoLocation = barringInfo != null
+                                ? barringInfo.createLocationInfoSanitizedCopy() : null;
+                        if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
+                        try {
+                            r.callback.onBarringInfoChanged(
+                                    checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
+                                            ? barringInfo : biNoLocation);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
+                        try {
+                            r.callback.onPhysicalChannelConfigurationChanged(
+                                    mPhysicalChannelConfigs);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                 }
             }
+        } else {
+            if(DBG) log("listen: Unregister");
+            remove(callback.asBinder());
         }
     }
 
@@ -1170,7 +1133,7 @@
                 // If any of the system clients wants to always listen to signal strength,
                 // we need to set it on.
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                        PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) {
                     telephonyManager.createForSubscriptionId(subscriptionId)
                             .setAlwaysReportSignalStrength(true);
                     return;
@@ -1271,7 +1234,7 @@
                     // strength is removed from registry records, we need to check if
                     // the signal strength decision needs to update on its slot.
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                            PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) {
                         updateReportSignalStrengthDecision(r.subId);
                     }
                     return;
@@ -1291,8 +1254,8 @@
 
         synchronized (mRecords) {
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
-                        && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+                if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
+                        (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                     try {
                         // Ensure the listener has read call log permission; if they do not return
                         // an empty phone number.
@@ -1326,9 +1289,9 @@
                 mCallState[phoneId] = state;
                 mCallIncomingNumber[phoneId] = incomingNumber;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
-                            && (r.subId == subId)
-                            && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
+                            (r.subId == subId) &&
+                            (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                         try {
                             String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId);
                             r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
@@ -1368,9 +1331,8 @@
                         log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " state=" + state);
                     }
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
+                            idMatch(r.subId, subId, phoneId)) {
 
                         try {
                             ServiceState stateToSend;
@@ -1431,7 +1393,7 @@
                     try {
                         if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
                                 && r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+                                        PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE)
                                 && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
@@ -1442,7 +1404,7 @@
                         }
                         if ((activationType == SIM_ACTIVATION_TYPE_DATA)
                                 && r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)
+                                        PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE)
                                 && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
@@ -1481,11 +1443,9 @@
                         log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
-                    if ((r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
+                    if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
                             || r.matchPhoneStateListenerEvent(
-                                    PhoneStateListener.
-                                            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
+                                    PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH))
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG) {
@@ -1498,9 +1458,8 @@
                             mRemoveList.add(r.binder);
                         }
                     }
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                             int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -1546,8 +1505,8 @@
                 }
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCarrierNetworkChange(active);
                         } catch (RemoteException ex) {
@@ -1577,10 +1536,9 @@
             if (validatePhoneId(phoneId)) {
                 mCellInfo.set(phoneId, cellInfo);
                 for (Record r : mRecords) {
-                    if (validateEventAndUserLocked(
-                            r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)
-                            && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+                    if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
+                            idMatch(r.subId, subId, phoneId) &&
+                            (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
                         try {
                             if (DBG_LOC) {
@@ -1612,8 +1570,8 @@
                 mMessageWaiting[phoneId] = mwi;
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onMessageWaitingIndicatorChanged(mwi);
                         } catch (RemoteException ex) {
@@ -1639,8 +1597,8 @@
                 mUserMobileDataState[phoneId] = state;
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onUserMobileDataStateChanged(state);
                         } catch (RemoteException ex) {
@@ -1678,7 +1636,7 @@
                 mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)
+                            PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
                             && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
                         try {
                             r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
@@ -1710,8 +1668,8 @@
                 mCallForwarding[phoneId] = cfi;
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallForwardingIndicatorChanged(cfi);
                         } catch (RemoteException ex) {
@@ -1738,9 +1696,8 @@
                 mDataActivity[phoneId] = state;
                 for (Record r : mRecords) {
                     // Notify by correct subId.
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onDataActivity(state);
                         } catch (RemoteException ex) {
@@ -1787,7 +1744,7 @@
                     mLocalLog.log(str);
                     for (Record r : mRecords) {
                         if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)
+                                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE)
                                 && idMatch(r.subId, subId, phoneId)) {
                             try {
                                 if (DBG) {
@@ -1812,7 +1769,7 @@
                 if (!Objects.equals(oldState, preciseState)) {
                     for (Record r : mRecords) {
                         if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
+                                PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
                                 && idMatch(r.subId, subId, phoneId)) {
                             try {
                                 r.callback.onPreciseDataConnectionStateChanged(preciseState);
@@ -1857,10 +1814,9 @@
             if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) {
                 mCellIdentity[phoneId] = cellIdentity;
                 for (Record r : mRecords) {
-                    if (validateEventAndUserLocked(
-                            r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)
-                            && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+                    if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
+                            idMatch(r.subId, subId, phoneId) &&
+                            (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
                         try {
                             if (DBG_LOC) {
@@ -1911,8 +1867,7 @@
                 }
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)
+                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
@@ -1921,7 +1876,7 @@
                         }
                     }
                     if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+                            PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -1970,7 +1925,7 @@
                 mImsReasonInfo.set(phoneId, imsReasonInfo);
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
+                            PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2002,8 +1957,8 @@
                 mSrvccState[phoneId] = state;
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) &&
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r);
@@ -2031,7 +1986,7 @@
                         log("notifyOemHookRawEventForSubscriber:  r=" + r + " subId=" + subId);
                     }
                     if ((r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_OEM_HOOK_RAW))
+                            PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT))
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onOemHookRawEvent(rawData);
@@ -2059,7 +2014,7 @@
 
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+                        PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) {
                     try {
                         r.callback.onPhoneCapabilityChanged(capability);
                     } catch (RemoteException ex) {
@@ -2084,7 +2039,7 @@
         synchronized (mRecords) {
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+                        PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) {
                     try {
                         r.callback.onActiveDataSubIdChanged(activeDataSubId);
                     } catch (RemoteException ex) {
@@ -2111,7 +2066,7 @@
 
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)
+                            PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onRadioPowerStateChanged(state);
@@ -2140,7 +2095,7 @@
 
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+                            PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -2172,7 +2127,7 @@
             for (Record r : mRecords) {
                 // Send to all listeners regardless of subscription
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) {
+                        PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) {
                     try {
                         r.callback.onOutgoingEmergencyCall(emergencyNumber, subId);
                     } catch (RemoteException ex) {
@@ -2196,7 +2151,7 @@
                 for (Record r : mRecords) {
                     // Send to all listeners regardless of subscription
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) {
+                            PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) {
                         try {
                             r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
                         } catch (RemoteException ex) {
@@ -2226,7 +2181,7 @@
 
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+                            PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -2257,7 +2212,7 @@
             if (validatePhoneId(phoneId)) {
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_REGISTRATION_FAILURE)
+                            PhoneStateListener.LISTEN_REGISTRATION_FAILURE)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onRegistrationFailed(
@@ -2300,7 +2255,7 @@
                 if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_BARRING_INFO_CHANGED)
+                            PhoneStateListener.LISTEN_BARRING_INFO)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2327,14 +2282,14 @@
      * @param subId the subId
      * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
      */
-    public void notifyPhysicalChannelConfigForSubscriber(
+    public void notifyPhysicalChannelConfigurationForSubscriber(
             int subId, List<PhysicalChannelConfig> configs) {
-        if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) {
+        if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
             return;
         }
 
         if (VDBG) {
-            log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs);
+            log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
         }
 
         synchronized (mRecords) {
@@ -2343,15 +2298,15 @@
                 mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
+                            PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
-                                log("notifyPhysicalChannelConfig: "
+                                log("notifyPhysicalChannelConfiguration: "
                                         + "mPhysicalChannelConfigs="
                                         + configs + " r=" + r);
                             }
-                            r.callback.onPhysicalChannelConfigChanged(configs);
+                            r.callback.onPhysicalChannelConfigurationChanged(configs);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2661,18 +2616,18 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
-                                            @Nullable String callingFeatureId, String message) {
+    private boolean checkListenerPermission(long events, int subId, String callingPackage,
+            @Nullable String callingFeatureId, String message) {
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
-                        .setCallingPackage(callingPackage)
-                        .setMethod(message + " events: " + events)
-                        .setCallingPid(Binder.getCallingPid())
-                        .setCallingUid(Binder.getCallingUid());
+                .setCallingPackage(callingPackage)
+                .setMethod(message + " events: " + events)
+                .setCallingPid(Binder.getCallingPid())
+                .setCallingUid(Binder.getCallingUid());
 
         boolean shouldCheckLocationPermissions = false;
 
-        if (isLocationPermissionRequired(events)) {
+        if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) {
             // Everything that requires fine location started in Q. So far...
             locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
             // If we're enforcing fine starting in Q, we also want to enforce coarse even for
@@ -2697,14 +2652,14 @@
             }
         }
 
-        if (isPhoneStatePermissionRequired(events)) {
+        if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                     mContext, subId, callingPackage, callingFeatureId, message)) {
                 isPermissionCheckSuccessful = false;
             }
         }
 
-        if (isPrecisePhoneStatePermissionRequired(events)) {
+        if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
             // check if calling app has either permission READ_PRECISE_PHONE_STATE
             // or with carrier privileges
             try {
@@ -2715,20 +2670,21 @@
             }
         }
 
-        if (isActiveEmergencySessionPermissionRequired(events)) {
+        if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
         }
 
-        if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
+        if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
         }
 
-        if (isPrivilegedPhoneStatePermissionRequired(events)) {
+        if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
+
         return isPermissionCheckSuccessful;
     }
 
@@ -2736,25 +2692,25 @@
         int size = mRemoveList.size();
         if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size);
         if (size > 0) {
-            for (IBinder b : mRemoveList) {
+            for (IBinder b: mRemoveList) {
                 remove(b);
             }
             mRemoveList.clear();
         }
     }
 
-    private boolean validateEventAndUserLocked(Record r, int event) {
+    private boolean validateEventsAndUserLocked(Record r, int events) {
         int foregroundUser;
         final long callingIdentity = Binder.clearCallingIdentity();
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
             valid = UserHandle.getUserId(r.callerUid) == foregroundUser
-                    && r.matchPhoneStateListenerEvent(event);
+                    && r.matchPhoneStateListenerEvent(events);
             if (DBG | DBG_LOC) {
-                log("validateEventAndUserLocked: valid=" + valid
+                log("validateEventsAndUserLocked: valid=" + valid
                         + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
-                        + " r.eventList=" + r.eventList + " event=" + event);
+                        + " r.events=" + r.events + " events=" + events);
             }
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
@@ -2866,14 +2822,9 @@
     }
 
     private void checkPossibleMissNotify(Record r, int phoneId) {
-        Set<Integer> events = r.eventList;
+        long events = r.events;
 
-        if (events == null || events.isEmpty()) {
-            log("checkPossibleMissNotify: events = null.");
-            return;
-        }
-
-        if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) {
+        if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
             try {
                 if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" +
                         mServiceState[phoneId]);
@@ -2892,9 +2843,8 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
-                || events.contains(
-                PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0
+                || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     SignalStrength signalStrength = mSignalStrength[phoneId];
@@ -2909,7 +2859,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     int gsmSignalStrength = mSignalStrength[phoneId]
@@ -2926,7 +2876,7 @@
             }
         }
 
-        if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+        if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
             try {
                 if (DBG_LOC) {
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
@@ -2941,7 +2891,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
@@ -2953,7 +2903,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
@@ -2967,7 +2917,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId="
@@ -2980,7 +2930,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId="
@@ -2993,7 +2943,7 @@
             }
         }
 
-        if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+        if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
             try {
                 if (DBG_LOC) {
                     log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = "
@@ -3009,7 +2959,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+        if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
             try {
                 if (DBG) {
                     log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState"
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f376473..165b6a1 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -165,9 +165,13 @@
         // TODO: Clear VCN configuration, trigger teardown as necessary
     }
 
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
-    class VcnNetworkProvider extends NetworkProvider {
-        VcnNetworkProvider(@NonNull Context context, @NonNull Looper looper) {
+    /**
+     * Network provider for VCN networks.
+     *
+     * @hide
+     */
+    public class VcnNetworkProvider extends NetworkProvider {
+        VcnNetworkProvider(Context context, Looper looper) {
             super(context, looper, VcnNetworkProvider.class.getSimpleName());
         }
 
diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java
index 7691852..d7e95df 100644
--- a/services/core/java/com/android/server/adb/AdbShellCommand.java
+++ b/services/core/java/com/android/server/adb/AdbShellCommand.java
@@ -16,7 +16,7 @@
 
 package com.android.server.adb;
 
-import android.os.BasicShellCommandHandler;
+import com.android.modules.utils.BasicShellCommandHandler;
 
 import java.io.PrintWriter;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 01e77f8..d6f7299 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1630,6 +1630,8 @@
         }
     }
 
+    // TODO: remove as part of fixing b/173627642
+    @SuppressWarnings("AndroidFrameworkCompatChange")
     private void postFgsNotificationLocked(ServiceRecord r) {
         boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled;
         if (!showNow) {
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index c86c29f..27be53a 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -52,9 +52,8 @@
 
     void clear() {
         mActiveUids.clear();
-        if (mPostChangesToAtm) {
-            mService.mAtmInternal.onActiveUidsCleared();
-        }
+        // It is only called for a temporal container with mPostChangesToAtm == false or test case.
+        // So there is no need to notify activity task manager.
     }
 
     UidRecord get(int uid) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6765132..ed2faf9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -37,7 +37,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
@@ -66,6 +65,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -519,16 +519,24 @@
         return data;
     }
 
-    public ParcelFileDescriptor getStatisticsStream() {
+    /**
+     * Returns parceled BatteryStats as a MemoryFile.
+     *
+     * @param forceUpdate If true, runs a sync to get fresh battery stats. Otherwise,
+     *                  returns the current values.
+     */
+    public ParcelFileDescriptor getStatisticsStream(boolean forceUpdate) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        // Drain the handler queue to make sure we've handled all pending works, so we'll get
-        // an accurate stats.
-        awaitCompletion();
-        syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
+        if (forceUpdate) {
+            // Drain the handler queue to make sure we've handled all pending works, so we'll get
+            // an accurate stats.
+            awaitCompletion();
+            syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
+        }
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
         }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 057b007c..364ad21 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -806,6 +806,7 @@
     void updateKeepWarmLocked() {
         mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
                 && (ams.mUserController.getCurrentUserId() == userId
+                || ams.mUserController.isCurrentProfile(userId)
                 || ams.isSingleton(processName, appInfo, instanceName.getClassName(),
                         serviceInfo.flags));
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4c5b747..12fcc9c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -80,7 +80,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.text.format.DateUtils;
@@ -103,6 +102,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.UserState.KeyEvictedCallback;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 09937e3..da47040 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -94,7 +94,6 @@
 import android.app.RuntimeAppOpAccessMessage;
 import android.app.SyncNotedAppOp;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -3018,28 +3017,6 @@
         }
     }
 
-    private boolean isTrustedVoiceServiceProxy(String packageName, int code) {
-        if (code != OP_RECORD_AUDIO) {
-            return false;
-        }
-        final String voiceRecognitionComponent = Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
-        final String voiceInteractionComponent = Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE);
-
-        final String voiceRecognitionServicePackageName =
-                getComponentPackageNameFromString(voiceRecognitionComponent);
-        final String voiceInteractionServicePackageName =
-                getComponentPackageNameFromString(voiceInteractionComponent);
-        return Objects.equals(packageName, voiceRecognitionServicePackageName) && Objects.equals(
-                voiceRecognitionServicePackageName, voiceInteractionServicePackageName);
-    }
-
-    private String getComponentPackageNameFromString(String from) {
-        ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
-        return componentName != null ? componentName.getPackageName() : "";
-    }
-
     @Override
     public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
             String proxiedAttributionTag, int proxyUid, String proxyPackageName,
@@ -3057,10 +3034,12 @@
 
         // This is a workaround for R QPR, new API change is not allowed. We only allow the current
         // voice recognizer is also the voice interactor to noteproxy op.
-        final boolean isTrustVoiceServiceProxy = isTrustedVoiceServiceProxy(proxyPackageName, code);
+        final boolean isTrustVoiceServiceProxy =
+                AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code);
+        final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy;
+                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
 
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
@@ -3524,10 +3503,12 @@
 
         // This is a workaround for R QPR, new API change is not allowed. We only allow the current
         // voice recognizer is also the voice interactor to noteproxy op.
-        final boolean isTrustVoiceServiceProxy = isTrustedVoiceServiceProxy(proxyPackageName, code);
+        final boolean isTrustVoiceServiceProxy =
+                AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code);
+        final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy;
+                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
 
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 84de25c..72d3835 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -48,10 +48,10 @@
             ]
         },
         {
-            "name": "CtsStatsdHostTestCases",
+            "name": "CtsStatsdAtomHostTestCases",
             "options": [
                 {
-                    "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps"
+                    "include-filter": "android.cts.statsdatom.appops.AppOpsTests#testAppOps"
                 }
             ]
         }        
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 52fc93f..89150ae 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -121,8 +121,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -151,6 +149,8 @@
 import com.android.server.SystemService;
 import com.android.server.audio.AudioServiceEvents.PhoneStateEvent;
 import com.android.server.audio.AudioServiceEvents.VolumeEvent;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 2784f46..b430521 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -543,6 +543,10 @@
         return mCurrentOperation.clientMonitor;
     }
 
+    public int getCurrentPendingCount() {
+        return mPendingOperations.size();
+    }
+
     public void recordCrashState() {
         if (mCrashStates.size() >= CrashState.NUM_ENTRIES) {
             mCrashStates.removeFirst();
@@ -564,8 +568,22 @@
 
     public void dump(PrintWriter pw) {
         pw.println("Dump of BiometricScheduler " + getTag());
+        pw.println("Current operation: " + mCurrentOperation);
+        pw.println("Pending operations: " + mPendingOperations.size());
+        for (Operation operation : mPendingOperations) {
+            pw.println("Pending operation: " + operation);
+        }
         for (CrashState crashState : mCrashStates) {
             pw.println("Crash State " + crashState);
         }
     }
+
+    /**
+     * Clears the scheduler of anything work-related. This should be used for example when the
+     * HAL dies.
+     */
+    public void reset() {
+        mPendingOperations.clear();
+        mCurrentOperation = null;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 0dee816..bbd6523 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -219,7 +219,7 @@
         return mSensorId;
     }
 
-    public final T getFreshDaemon() {
+    public T getFreshDaemon() {
         return mLazyDaemon.getDaemon();
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 4906aac..47897a1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -64,6 +64,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -425,6 +426,13 @@
                             provider.dumpProtoMetrics(props.sensorId, fd);
                         }
                     }
+                } else if (args.length > 1 && "--hal".equals(args[0])) {
+                    for (ServiceProvider provider : mServiceProviders) {
+                        for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
+                            provider.dumpHal(props.sensorId, fd,
+                                    Arrays.copyOfRange(args, 1, args.length, args.getClass()));
+                        }
+                    }
                 } else {
                     for (ServiceProvider provider : mServiceProviders) {
                         for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index d1578fa..be4e248 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -118,4 +118,6 @@
 
     @NonNull
     ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
+
+    void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java
new file mode 100644
index 0000000..77e6ceb
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.face.aidl;
+
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * A utility class for the AIDL implementation of NativeHandle - {@link
+ * android.hardware.common.NativeHandle}.
+ */
+public final class AidlNativeHandleUtils {
+
+    /**
+     * Converts a {@link android.os.NativeHandle} to a {@link android.hardware.common.NativeHandle}
+     * by duplicating the underlying file descriptors.
+     *
+     * Both the original and new handle must be closed after use.
+     *
+     * @param handle {@link android.os.NativeHandle}. Can be null.
+     * @return a {@link android.hardware.common.NativeHandle} representation of {@code handle}.
+     * Returns null if {@code handle} is null.
+     * @throws IOException if any of the underlying calls to {@code dup} fail.
+     */
+    public static @Nullable android.hardware.common.NativeHandle dup(
+            @Nullable android.os.NativeHandle handle) throws IOException {
+        if (handle == null) {
+            return null;
+        }
+        android.hardware.common.NativeHandle res = new android.hardware.common.NativeHandle();
+        final FileDescriptor[] fds = handle.getFileDescriptors();
+        res.ints = handle.getInts().clone();
+        res.fds = new ParcelFileDescriptor[fds.length];
+        for (int i = 0; i < fds.length; ++i) {
+            res.fds[i] = ParcelFileDescriptor.dup(fds[i]);
+        }
+        return res;
+    }
+
+    /**
+     * Closes the file descriptors contained within a {@link android.hardware.common.NativeHandle}.
+     * This is a no-op if the handle is null.
+     *
+     * This should only be used for handles that own their file descriptors, for example handles
+     * obtained using {@link #dup(android.os.NativeHandle)}.
+     *
+     * @param handle {@link android.hardware.common.NativeHandle}. Can be null.
+     * @throws IOException if any of the underlying calls to {@code close} fail.
+     */
+    public static void close(@Nullable android.hardware.common.NativeHandle handle)
+            throws IOException {
+        if (handle != null) {
+            for (ParcelFileDescriptor fd : handle.fds) {
+                if (fd != null) {
+                    fd.close();
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
new file mode 100644
index 0000000..13bbb7e8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -0,0 +1,205 @@
+/*
+ * 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.face.aidl;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A test session implementation for {@link FaceProvider}. See
+ * {@link android.hardware.biometrics.BiometricTestSession}.
+ */
+public class BiometricTestSessionImpl extends ITestSession.Stub {
+
+    private static final String TAG = "BiometricTestSessionImpl";
+
+    @NonNull private final Context mContext;
+    private final int mSensorId;
+    @NonNull private final FaceProvider mProvider;
+    @NonNull private final Sensor mSensor;
+    @NonNull private final Set<Integer> mEnrollmentIds;
+    @NonNull private final Random mRandom;
+
+    /**
+     * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
+     * test, since enrollment is a platform-only API. The authentication path is tested through
+     * the public BiometricPrompt APIs and does not use this receiver.
+     */
+    private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() {
+        @Override
+        public void onEnrollResult(Face face, int remaining) {
+
+        }
+
+        @Override
+        public void onAcquired(int acquireInfo, int vendorCode) {
+
+        }
+
+        @Override
+        public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
+
+        }
+
+        @Override
+        public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
+
+        }
+
+        @Override
+        public void onAuthenticationFailed() {
+
+        }
+
+        @Override
+        public void onError(int error, int vendorCode) {
+
+        }
+
+        @Override
+        public void onRemoved(Face face, int remaining) {
+
+        }
+
+        @Override
+        public void onFeatureSet(boolean success, int feature) {
+
+        }
+
+        @Override
+        public void onFeatureGet(boolean success, int feature, boolean value) {
+
+        }
+
+        @Override
+        public void onChallengeGenerated(int sensorId, long challenge) {
+
+        }
+
+        @Override
+        public void onChallengeInterrupted(int sensorId) {
+
+        }
+
+        @Override
+        public void onChallengeInterruptFinished(int sensorId) {
+
+        }
+    };
+
+    BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+            @NonNull FaceProvider provider, @NonNull Sensor sensor) {
+        mContext = context;
+        mSensorId = sensorId;
+        mProvider = provider;
+        mSensor = sensor;
+        mEnrollmentIds = new HashSet<>();
+        mRandom = new Random();
+    }
+
+    @Override
+    public void setTestHalEnabled(boolean enabled) {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mProvider.setTestHalEnabled(enabled);
+        mSensor.setTestHalEnabled(enabled);
+    }
+
+    @Override
+    public void startEnroll(int userId) {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
+                mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */);
+    }
+
+    @Override
+    public void finishEnroll(int userId) {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        int nextRandomId = mRandom.nextInt();
+        while (mEnrollmentIds.contains(nextRandomId)) {
+            nextRandomId = mRandom.nextInt();
+        }
+
+        mEnrollmentIds.add(nextRandomId);
+        mSensor.getSessionForUser(userId).mHalSessionCallback
+                .onEnrollmentProgress(nextRandomId, 0 /* remaining */);
+    }
+
+    @Override
+    public void acceptAuthentication(int userId)  {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        // Fake authentication with any of the existing faces
+        List<Face> faces = FaceUtils.getInstance(mSensorId)
+                .getBiometricsForUser(mContext, userId);
+        if (faces.isEmpty()) {
+            Slog.w(TAG, "No faces, returning");
+            return;
+        }
+        final int fid = faces.get(0).getBiometricId();
+        mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid,
+                HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69]));
+    }
+
+    @Override
+    public void rejectAuthentication(int userId)  {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
+    }
+
+    @Override
+    public void notifyAcquired(int userId, int acquireInfo)  {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mSensor.getSessionForUser(userId).mHalSessionCallback
+                .onAcquired((byte) acquireInfo, 0 /* vendorCode */);
+    }
+
+    @Override
+    public void notifyError(int userId, int errorCode)  {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode,
+                0 /* vendorCode */);
+    }
+
+    @Override
+    public void cleanupInternalState(int userId)  {
+        Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+        mProvider.scheduleInternalCleanup(mSensorId, userId);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index f950862..f09df1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -39,6 +39,7 @@
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
+import java.io.IOException;
 import java.util.ArrayList;
 
 /**
@@ -51,6 +52,7 @@
     @NonNull private final int[] mEnrollIgnoreList;
     @NonNull private final int[] mEnrollIgnoreListVendor;
     @Nullable private ICancellationSignal mCancellationSignal;
+    @Nullable private android.hardware.common.NativeHandle mPreviewSurface;
     private final int mMaxTemplatesPerUser;
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@@ -66,6 +68,24 @@
         mEnrollIgnoreListVendor = getContext().getResources()
                 .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
         mMaxTemplatesPerUser = maxTemplatesPerUser;
+        try {
+            // We must manually close the duplicate handle after it's no longer needed.
+            // The caller is responsible for closing the original handle.
+            mPreviewSurface = AidlNativeHandleUtils.dup(previewSurface);
+        } catch (IOException e) {
+            mPreviewSurface = null;
+            Slog.e(TAG, "Failed to dup previewSurface", e);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        try {
+            AidlNativeHandleUtils.close(mPreviewSurface);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to close mPreviewSurface", e);
+        }
+        super.destroy();
     }
 
     @Override
@@ -94,10 +114,9 @@
 
         try {
             // TODO(b/172593978): Pass features.
-            // TODO(b/172593521): Pass mPreviewSurface as android.hardware.common.NativeHandle.
             mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
                     HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
-                    null /* mPreviewSurface */);
+                    mPreviewSurface);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enroll", e);
             onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 3280e98..c27b6e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -47,6 +47,11 @@
         // Nothing to do here
     }
 
+    public void start(@NonNull Callback callback) {
+        super.start(callback);
+        startHalOperation();
+    }
+
     @Override
     protected void startHalOperation() {
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 01c16fd..36796b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -41,6 +41,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.ClientMonitor;
@@ -67,9 +68,12 @@
     private static final String TAG = "FaceProvider";
     private static final int ENROLL_TIMEOUT_SEC = 75;
 
+    private boolean mTestHalEnabled;
+
     @NonNull private final Context mContext;
     @NonNull private final String mHalInstanceName;
-    @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+    @NonNull @VisibleForTesting
+    final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
     @NonNull private final ClientMonitor.LazyDaemon<IFace> mLazyDaemon;
     @NonNull private final Handler mHandler;
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -150,6 +154,10 @@
 
     @Nullable
     private synchronized IFace getHalInstance() {
+        if (mTestHalEnabled) {
+            return new TestHal();
+        }
+
         if (mDaemon != null) {
             return mDaemon;
         }
@@ -197,7 +205,7 @@
 
     private void createNewSessionWithoutHandler(@NonNull IFace daemon, int sensorId,
             int userId) throws RemoteException {
-        // Note that per IFingerprint createSession contract, this method will block until all
+        // Note that per IFace createSession contract, this method will block until all
         // existing operations are canceled/finished. However, also note that this is fine, since
         // this method "withoutHandler" means it should only ever be invoked from the worker thread,
         // so callers will never be blocked.
@@ -525,7 +533,9 @@
 
     @Override
     public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
-
+        if (mSensors.contains(sensorId)) {
+            mSensors.get(sensorId).dumpProtoState(sensorId, proto);
+        }
     }
 
     @Override
@@ -576,18 +586,28 @@
     @NonNull
     @Override
     public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
-        return null; // TODO
+        return mSensors.get(sensorId).createTestSession();
     }
 
     @Override
+    public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {}
+
+    @Override
     public void binderDied() {
         Slog.e(getTag(), "HAL died");
         mHandler.post(() -> {
             mDaemon = null;
             for (int i = 0; i < mSensors.size(); i++) {
+                final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+                sensor.getScheduler().recordCrashState();
+                sensor.getScheduler().reset();
             }
         });
     }
+
+    void setTestHalEnabled(boolean enabled) {
+        mTestHalEnabled = enabled;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 5d100ec..5b1f546 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -61,6 +61,11 @@
         // Nothing to do here
     }
 
+    public void start(@NonNull Callback callback) {
+        super.start(callback);
+        startHalOperation();
+    }
+
     @Override
     protected void startHalOperation() {
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 2dd6e73..d9d4737 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ITestSession;
 import android.hardware.biometrics.face.Error;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
@@ -31,10 +33,15 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.SensorServiceStateProto;
+import com.android.server.biometrics.SensorStateProto;
+import com.android.server.biometrics.UserStateProto;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -56,6 +63,8 @@
  */
 public class Sensor implements IBinder.DeathRecipient {
 
+    private boolean mTestHalEnabled;
+
     @NonNull private final String mTag;
     @NonNull private final FaceProvider mProvider;
     @NonNull private final Context mContext;
@@ -67,27 +76,6 @@
     @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
     @Nullable private Session mCurrentSession;
 
-    @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(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-
-                mScheduler.recordCrashState();
-
-                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.MODALITY_FACE,
-                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
-                mCurrentSession = null;
-            }
-        });
-    }
-
     static class Session {
         @NonNull final HalSessionCallback mHalSessionCallback;
         @NonNull private final String mTag;
@@ -104,67 +92,6 @@
         }
     }
 
-    Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
-            @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
-        mTag = tag;
-        mProvider = provider;
-        mContext = context;
-        mHandler = handler;
-        mSensorProperties = sensorProperties;
-        mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
-        mLockoutCache = new LockoutCache();
-        mAuthenticatorIds = new HashMap<>();
-        mLazySession = () -> (mCurrentSession != null) ? mCurrentSession.mSession : null;
-    }
-
-    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
-        return mLazySession;
-    }
-
-    @NonNull FaceSensorPropertiesInternal getSensorProperties() {
-        return mSensorProperties;
-    }
-
-    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
-    boolean hasSessionForUser(int userId) {
-        return mCurrentSession != null && mCurrentSession.mUserId == userId;
-    }
-
-    @Nullable Session getSessionForUser(int userId) {
-        if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
-            return mCurrentSession;
-        } else {
-            return null;
-        }
-    }
-
-    void createNewSession(@NonNull IFace daemon, int sensorId, int userId)
-            throws RemoteException {
-
-        final HalSessionCallback.Callback callback = () -> {
-            Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
-            mCurrentSession = null;
-        };
-        final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
-                mTag, mScheduler, sensorId, userId, callback);
-
-        final ISession newSession = daemon.createSession(sensorId, userId, resultController);
-        newSession.asBinder().linkToDeath(this, 0 /* flags */);
-        mCurrentSession = new Session(mTag, newSession, userId, resultController);
-    }
-
-    @NonNull BiometricScheduler getScheduler() {
-        return mScheduler;
-    }
-
-    @NonNull LockoutCache getLockoutCache() {
-        return mLockoutCache;
-    }
-
-    @NonNull Map<Integer, Long> getAuthenticatorIds() {
-        return mAuthenticatorIds;
-    }
-
     static class HalSessionCallback extends ISessionCallback.Stub {
         /**
          * Interface to sends results to the HalSessionCallback's owner.
@@ -453,4 +380,120 @@
         }
 
     }
+
+    Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+            @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
+        mTag = tag;
+        mProvider = provider;
+        mContext = context;
+        mHandler = handler;
+        mSensorProperties = sensorProperties;
+        mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
+        mLockoutCache = new LockoutCache();
+        mAuthenticatorIds = new HashMap<>();
+        mLazySession = () -> {
+            if (mTestHalEnabled) {
+                return new TestSession(mCurrentSession.mHalSessionCallback);
+            } else {
+                return mCurrentSession != null ? mCurrentSession.mSession : null;
+            }
+        };
+    }
+
+    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+        return mLazySession;
+    }
+
+    @NonNull FaceSensorPropertiesInternal getSensorProperties() {
+        return mSensorProperties;
+    }
+
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    boolean hasSessionForUser(int userId) {
+        return mCurrentSession != null && mCurrentSession.mUserId == userId;
+    }
+
+    @Nullable Session getSessionForUser(int userId) {
+        if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
+            return mCurrentSession;
+        } else {
+            return null;
+        }
+    }
+
+    @NonNull ITestSession createTestSession() {
+        return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
+    }
+
+    void createNewSession(@NonNull IFace daemon, int sensorId, int userId)
+            throws RemoteException {
+
+        final HalSessionCallback.Callback callback = () -> {
+            Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+            mCurrentSession = null;
+        };
+        final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
+                mTag, mScheduler, sensorId, userId, callback);
+
+        final ISession newSession = daemon.createSession(sensorId, userId, resultController);
+        newSession.asBinder().linkToDeath(this, 0 /* flags */);
+        mCurrentSession = new Session(mTag, newSession, userId, resultController);
+    }
+
+    @NonNull BiometricScheduler getScheduler() {
+        return mScheduler;
+    }
+
+    @NonNull LockoutCache getLockoutCache() {
+        return mLockoutCache;
+    }
+
+    @NonNull Map<Integer, Long> getAuthenticatorIds() {
+        return mAuthenticatorIds;
+    }
+
+    void setTestHalEnabled(boolean enabled) {
+        mTestHalEnabled = enabled;
+    }
+
+    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+        final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
+
+        proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+
+        for (UserInfo user : UserManager.get(mContext).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(SensorStateProto.USER_STATES);
+            proto.write(UserStateProto.USER_ID, userId);
+            proto.write(UserStateProto.NUM_ENROLLED,
+                    FaceUtils.getInstance(mSensorProperties.sensorId)
+                            .getBiometricsForUser(mContext, userId).size());
+            proto.end(userToken);
+        }
+
+        proto.end(sensorToken);
+    }
+
+    @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(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+
+                mScheduler.recordCrashState();
+
+                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        BiometricsProtoEnums.MODALITY_FACE,
+                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
+                mCurrentSession = null;
+            }
+        });
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
new file mode 100644
index 0000000..34bf9bc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -0,0 +1,98 @@
+/*
+ * 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.face.aidl;
+
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.ISessionCallback;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Test HAL that provides only no-ops.
+ */
+public class TestHal extends IFace.Stub {
+    @Override
+    public SensorProps[] getSensorProps() {
+        return new SensorProps[0];
+    }
+
+    @Override
+    public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
+        return new ISession() {
+            @Override
+            public void generateChallenge(int cookie, int timeoutSec) {
+
+            }
+
+            @Override
+            public void revokeChallenge(int cookie, long challenge) {
+
+            }
+
+            @Override
+            public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+                    NativeHandle previewSurface) {
+                return null;
+            }
+
+            @Override
+            public ICancellationSignal authenticate(int cookie, long operationId) {
+                return null;
+            }
+
+            @Override
+            public ICancellationSignal detectInteraction(int cookie) {
+                return null;
+            }
+
+            @Override
+            public void enumerateEnrollments(int cookie) {
+
+            }
+
+            @Override
+            public void removeEnrollments(int cookie, int[] enrollmentIds) {
+
+            }
+
+            @Override
+            public void getAuthenticatorId(int cookie) {
+
+            }
+
+            @Override
+            public void invalidateAuthenticatorId(int cookie) {
+
+            }
+
+            @Override
+            public void resetLockout(int cookie, HardwareAuthToken hat) {
+
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return new Binder();
+            }
+        };
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
new file mode 100644
index 0000000..9707edd
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.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.server.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Test session that provides mostly no-ops.
+ */
+public class TestSession extends ISession.Stub {
+    private static final String TAG = "TestSession";
+
+    @NonNull
+    private final Sensor.HalSessionCallback mHalSessionCallback;
+
+    TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
+        mHalSessionCallback = halSessionCallback;
+    }
+
+    @Override
+    public void generateChallenge(int cookie, int timeoutSec) {
+        mHalSessionCallback.onChallengeGenerated(0 /* challenge */);
+    }
+
+    @Override
+    public void revokeChallenge(int cookie, long challenge) {
+        mHalSessionCallback.onChallengeRevoked(challenge);
+    }
+
+    @Override
+    public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+            NativeHandle previewSurface) {
+        return null;
+    }
+
+    @Override
+    public ICancellationSignal authenticate(int cookie, long operationId) {
+        return new ICancellationSignal() {
+            @Override
+            public void cancel() throws RemoteException {
+                mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */);
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return new Binder();
+            }
+        };
+    }
+
+    @Override
+    public ICancellationSignal detectInteraction(int cookie) {
+        return null;
+    }
+
+    @Override
+    public void enumerateEnrollments(int cookie) {
+
+    }
+
+    @Override
+    public void removeEnrollments(int cookie, int[] enrollmentIds) {
+
+    }
+
+    @Override
+    public void getAuthenticatorId(int cookie) {
+        mHalSessionCallback.onAuthenticatorIdRetrieved(0);
+    }
+
+    @Override
+    public void invalidateAuthenticatorId(int cookie) {
+
+    }
+
+    @Override
+    public void resetLockout(int cookie, HardwareAuthToken hat) {
+        mHalSessionCallback.onLockoutCleared();
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 27ca33d..c57ab50 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -71,7 +71,6 @@
 import com.android.server.biometrics.sensors.face.LockoutHalImpl;
 import com.android.server.biometrics.sensors.face.ServiceProvider;
 import com.android.server.biometrics.sensors.face.UsageStats;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -259,8 +258,7 @@
 
                 if (!removed.isEmpty()) {
                     // Convert to old fingerprint-like behavior, where remove() receives
-                    // one removal
-                    // at a time. This way, remove can share some more common code.
+                    // one removal at a time. This way, remove can share some more common code.
                     for (int i = 0; i < removed.size(); i++) {
                         final int id = removed.get(i);
                         final Face face = new Face("", id, deviceId);
@@ -290,21 +288,16 @@
                 final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
 
                 if (!faceIds.isEmpty()) {
-                    // Convert to old fingerprint-like behavior, where enumerate()
-                    // receives one
-                    // template at a time. This way, enumerate can share some more common
-                    // code.
+                    // Convert to old fingerprint-like behavior, where enumerate() receives one
+                    // template at a time. This way, enumerate can share some more common code.
                     for (int i = 0; i < faceIds.size(); i++) {
                         final Face face = new Face("", faceIds.get(i), deviceId);
                         enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
                     }
                 } else {
-                    // For face, the HIDL contract is to receive an empty list when there
-                    // are no
-                    // templates enrolled. Send a null identifier since we don't consume
-                    // them
-                    // anywhere, and send remaining == 0 so this code can be shared with
-                    // Fingerprint@2.1
+                    // For face, the HIDL contract is to receive an empty list when there are no
+                    // templates enrolled. Send a null identifier since we don't consume them
+                    // anywhere, and send remaining == 0 so this code can be shared with Face@1.1
                     enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
                 }
             });
@@ -333,16 +326,17 @@
     }
 
     @VisibleForTesting
-    public Face10(@NonNull Context context, int sensorId,
+    Face10(@NonNull Context context, int sensorId,
             @BiometricManager.Authenticators.Types int strength,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            boolean supportsSelfIllumination, int maxTemplatesAllowed) {
+            boolean supportsSelfIllumination, int maxTemplatesAllowed,
+            @NonNull BiometricScheduler scheduler) {
         mSensorProperties = new FaceSensorPropertiesInternal(sensorId,
                 Utils.authenticatorStrengthToPropertyStrength(strength),
                 maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination);
         mContext = context;
         mSensorId = sensorId;
-        mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
+        mScheduler = scheduler;
         mHandler = new Handler(Looper.getMainLooper());
         mUsageStats = new UsageStats(context);
         mAuthenticatorIds = new HashMap<>();
@@ -369,7 +363,8 @@
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
         this(context, sensorId, strength, lockoutResetDispatcher,
                 context.getResources().getBoolean(R.bool.config_faceAuthSupportsSelfIllumination),
-                context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser));
+                context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser),
+                new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */));
     }
 
     @Override
@@ -388,12 +383,13 @@
                 interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
-
                 FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                         BiometricsProtoEnums.MODALITY_FACE,
                         BiometricsProtoEnums.ISSUE_HAL_DEATH);
             }
+
+            mScheduler.recordCrashState();
+            mScheduler.reset();
         });
     }
 
@@ -874,7 +870,10 @@
         });
     }
 
-    public void dumpHal(@NonNull FileDescriptor fd, @NonNull String[] args) {
+    /**
+     * Sends a debug message to the HAL with the provided FileDescriptor and arguments.
+     */
+    public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {
         // WARNING: CDD restricts image data from leaving TEE unencrypted on
         //          production devices:
         // [C-1-10] MUST not allow unencrypted access to identifiable biometric
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 9731325..3f9aef2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -143,7 +143,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         // Fake authentication with any of the existing fingers
-        List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
+        List<Fingerprint> fingerprints = FingerprintUtils.getInstance(mSensorId)
                 .getBiometricsForUser(mContext, userId);
         if (fingerprints.isEmpty()) {
             Slog.w(TAG, "No fingerprints, returning");
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 437ecd7..fd1181b 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
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.common.ICancellationSignal;
@@ -89,7 +88,8 @@
                     HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enroll", e);
-            onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+            onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+                    0 /* vendorCode */);
             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 5f3be488c1..db34d144 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
@@ -41,6 +41,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.ClientMonitor;
@@ -67,7 +68,8 @@
 
     @NonNull private final Context mContext;
     @NonNull private final String mHalInstanceName;
-    @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+    @NonNull @VisibleForTesting
+    final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
     @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
     @NonNull private final Handler mHandler;
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -607,8 +609,11 @@
             mDaemon = null;
 
             for (int i = 0; i < mSensors.size(); i++) {
+                final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+                sensor.getScheduler().recordCrashState();
+                sensor.getScheduler().reset();
             }
         });
     }
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 380608f..ecb9985 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
@@ -80,27 +80,6 @@
     @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;
-            }
-        });
-    }
-
     static class Session {
         @NonNull private final String mTag;
         @NonNull private final ISession mSession;
@@ -501,11 +480,33 @@
 
             final long userToken = proto.start(SensorStateProto.USER_STATES);
             proto.write(UserStateProto.USER_ID, userId);
-            proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
-                    .getBiometricsForUser(mContext, userId).size());
+            proto.write(UserStateProto.NUM_ENROLLED,
+                    FingerprintUtils.getInstance(mSensorProperties.sensorId)
+                            .getBiometricsForUser(mContext, userId).size());
             proto.end(userToken);
         }
 
         proto.end(sensorToken);
     }
+
+    @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;
+            }
+        });
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
index dd9150a..d637878 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
@@ -23,7 +23,7 @@
 import android.util.Slog;
 
 /**
- * Test HAL that provides only provides mostly no-ops.
+ * Test session that provides mostly no-ops.
  */
 class TestSession extends ISession.Stub {
 
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 9924d47..11372a3 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
@@ -386,12 +386,13 @@
                 interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
-
                 FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                         BiometricsProtoEnums.MODALITY_FINGERPRINT,
                         BiometricsProtoEnums.ISSUE_HAL_DEATH);
             }
+
+            mScheduler.recordCrashState();
+            mScheduler.reset();
         });
     }
 
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 c750b90..2a89a88 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
@@ -65,7 +65,7 @@
         final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
                 .size();
         if (enrolled >= limit) {
-            Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
+            Slog.w(TAG, "Too many fingerprints registered, user: " + getTargetUserId());
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index a4ae9c8..2a81426 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.CameraSessionStats;
+import android.hardware.CameraStreamStats;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
 import android.media.AudioManager;
@@ -36,11 +38,13 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserManager;
+import android.stats.camera.nano.CameraStreamProto;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.framework.protobuf.nano.MessageNano;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
@@ -97,7 +101,10 @@
     @interface DeviceStateFlags {}
 
     // Maximum entries to keep in usage history before dumping out
-    private static final int MAX_USAGE_HISTORY = 100;
+    private static final int MAX_USAGE_HISTORY = 20;
+    // Number of stream statistics being dumped for each camera session
+    // Must be equal to number of CameraStreamProto in CameraActionEvent
+    private static final int MAX_STREAM_STATISTICS = 5;
 
     private final Context mContext;
     private final ServiceThread mHandlerThread;
@@ -123,7 +130,6 @@
     private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
     private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
 
-    private final MetricsLogger mLogger = new MetricsLogger();
     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
     private static final String NFC_SERVICE_BINDER_NAME = "nfc";
     private static final IBinder nfcInterfaceToken = new Binder();
@@ -137,27 +143,50 @@
      * Structure to track camera usage
      */
     private static class CameraUsageEvent {
+        public final String mCameraId;
         public final int mCameraFacing;
         public final String mClientName;
         public final int mAPILevel;
+        public final boolean mIsNdk;
+        public final int mAction;
+        public final int mLatencyMs;
+        public final int mOperatingMode;
 
         private boolean mCompleted;
+        public int mInternalReconfigure;
+        public long mRequestCount;
+        public long mResultErrorCount;
+        public boolean mDeviceError;
+        public List<CameraStreamStats> mStreamStats;
         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
 
-        public CameraUsageEvent(int facing, String clientName, int apiLevel) {
+        CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel,
+                boolean isNdk, int action, int latencyMs, int operatingMode) {
+            mCameraId = cameraId;
             mCameraFacing = facing;
             mClientName = clientName;
             mAPILevel = apiLevel;
             mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
             mCompleted = false;
+            mIsNdk = isNdk;
+            mAction = action;
+            mLatencyMs = latencyMs;
+            mOperatingMode = operatingMode;
         }
 
-        public void markCompleted() {
+        public void markCompleted(int internalReconfigure, long requestCount,
+                long resultErrorCount, boolean deviceError,
+                List<CameraStreamStats>  streamStats) {
             if (mCompleted) {
                 return;
             }
             mCompleted = true;
             mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
+            mInternalReconfigure = internalReconfigure;
+            mRequestCount = requestCount;
+            mResultErrorCount = resultErrorCount;
+            mDeviceError = deviceError;
+            mStreamStats = streamStats;
             if (CameraServiceProxy.DEBUG) {
                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
                         " was in use by " + mClientName + " for " +
@@ -211,19 +240,22 @@
         }
 
         @Override
-        public void notifyCameraState(String cameraId, int newCameraState, int facing,
-                String clientName, int apiLevel) {
+        public void notifyCameraState(CameraSessionStats cameraState) {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
                         " camera service UID!");
                 return;
             }
-            String state = cameraStateToString(newCameraState);
-            String facingStr = cameraFacingToString(facing);
-            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
-                    state + " for client " + clientName + " API Level " + apiLevel);
+            String state = cameraStateToString(cameraState.getNewCameraState());
+            String facingStr = cameraFacingToString(cameraState.getFacing());
+            if (DEBUG) {
+                Slog.v(TAG, "Camera " + cameraState.getCameraId()
+                        + " facing " + facingStr + " state now " + state
+                        + " for client " + cameraState.getClientName()
+                        + " API Level " + cameraState.getApiLevel());
+            }
 
-            updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
+            updateActivityCount(cameraState);
         }
     };
 
@@ -385,20 +417,80 @@
         private void logCameraUsageEvent(CameraUsageEvent e) {
             int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
             switch(e.mCameraFacing) {
-                case ICameraServiceProxy.CAMERA_FACING_BACK:
+                case CameraSessionStats.CAMERA_FACING_BACK:
                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
                     break;
-                case ICameraServiceProxy.CAMERA_FACING_FRONT:
+                case CameraSessionStats.CAMERA_FACING_FRONT:
                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
                     break;
-                case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
+                case CameraSessionStats.CAMERA_FACING_EXTERNAL:
                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
                     break;
                 default:
                     Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
             }
+
+            int streamCount = 0;
+            if (e.mStreamStats != null) {
+                streamCount = e.mStreamStats.size();
+            }
+            if (CameraServiceProxy.DEBUG) {
+                Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
+                        + " clientName " + e.mClientName
+                        + ", duration " + e.getDuration()
+                        + ", APILevel " + e.mAPILevel
+                        + ", cameraId " + e.mCameraId
+                        + ", facing " + facing
+                        + ", isNdk " + e.mIsNdk
+                        + ", latencyMs " + e.mLatencyMs
+                        + ", operatingMode " + e.mOperatingMode
+                        + ", internalReconfigure " + e.mInternalReconfigure
+                        + ", requestCount " + e.mRequestCount
+                        + ", resultErrorCount " + e.mResultErrorCount
+                        + ", deviceError " + e.mDeviceError
+                        + ", streamCount is " + streamCount);
+            }
+            // Convert from CameraStreamStats to CameraStreamProto
+            CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
+            for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
+                streamProtos[i] = new CameraStreamProto();
+                if (i < streamCount) {
+                    CameraStreamStats streamStats = e.mStreamStats.get(i);
+                    streamProtos[i].width = streamStats.getWidth();
+                    streamProtos[i].height = streamStats.getHeight();
+                    streamProtos[i].format = streamStats.getFormat();
+                    streamProtos[i].dataSpace = streamStats.getDataSpace();
+                    streamProtos[i].usage = streamStats.getUsage();
+                    streamProtos[i].requestCount = streamStats.getRequestCount();
+                    streamProtos[i].errorCount = streamStats.getErrorCount();
+                    streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
+                    streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
+                    streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
+
+                    if (CameraServiceProxy.DEBUG) {
+                        Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+                                + ", height " + streamProtos[i].height
+                                + ", format " + streamProtos[i].format
+                                + ", dataSpace " + streamProtos[i].dataSpace
+                                + ", usage " + streamProtos[i].usage
+                                + ", requestCount " + streamProtos[i].requestCount
+                                + ", errorCount " + streamProtos[i].errorCount
+                                + ", firstCaptureLatencyMillis "
+                                + streamProtos[i].firstCaptureLatencyMillis
+                                + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
+                                + ", maxAppBuffers " + streamProtos[i].maxAppBuffers);
+                    }
+                }
+            }
             FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
-                    e.mAPILevel, e.mClientName, facing);
+                    e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk,
+                    e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure,
+                    e.mRequestCount, e.mResultErrorCount, e.mDeviceError,
+                    streamCount, MessageNano.toByteArray(streamProtos[0]),
+                    MessageNano.toByteArray(streamProtos[1]),
+                    MessageNano.toByteArray(streamProtos[2]),
+                    MessageNano.toByteArray(streamProtos[3]),
+                    MessageNano.toByteArray(streamProtos[4]));
         }
     }
 
@@ -410,35 +502,6 @@
         synchronized(mLock) {
             // Randomize order of events so that it's not meaningful
             Collections.shuffle(mCameraUsageHistory);
-            for (CameraUsageEvent e : mCameraUsageHistory) {
-                if (DEBUG) {
-                    Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
-                            cameraFacingToString(e.mCameraFacing) + " for " +
-                            e.getDuration() + " ms");
-                }
-                int subtype = 0;
-                switch(e.mCameraFacing) {
-                    case ICameraServiceProxy.CAMERA_FACING_BACK:
-                        subtype = MetricsEvent.CAMERA_BACK_USED;
-                        break;
-                    case ICameraServiceProxy.CAMERA_FACING_FRONT:
-                        subtype = MetricsEvent.CAMERA_FRONT_USED;
-                        break;
-                    case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
-                        subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
-                        break;
-                    default:
-                        continue;
-                }
-                LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
-                        .setType(MetricsEvent.TYPE_ACTION)
-                        .setSubtype(subtype)
-                        .setLatency(e.getDuration())
-                        .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
-                        .setPackageName(e.mClientName);
-                mLogger.write(l);
-            }
-
             mLogWriterService.execute(new EventWriterTask(
                         new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
 
@@ -569,13 +632,25 @@
         return true;
     }
 
-    private void updateActivityCount(String cameraId, int newCameraState, int facing,
-            String clientName, int apiLevel) {
+    private void updateActivityCount(CameraSessionStats cameraState) {
+        String cameraId = cameraState.getCameraId();
+        int newCameraState = cameraState.getNewCameraState();
+        int facing = cameraState.getFacing();
+        String clientName = cameraState.getClientName();
+        int apiLevel = cameraState.getApiLevel();
+        boolean isNdk = cameraState.isNdk();
+        int sessionType = cameraState.getSessionType();
+        int internalReconfigureCount = cameraState.getInternalReconfigureCount();
+        int latencyMs = cameraState.getLatencyMs();
+        long requestCount = cameraState.getRequestCount();
+        long resultErrorCount = cameraState.getResultErrorCount();
+        boolean deviceError = cameraState.getDeviceErrorFlag();
+        List<CameraStreamStats> streamStats = cameraState.getStreamStats();
         synchronized(mLock) {
             // Update active camera list and notify NFC if necessary
             boolean wasEmpty = mActiveCameraUsage.isEmpty();
             switch (newCameraState) {
-                case ICameraServiceProxy.CAMERA_STATE_OPEN:
+                case CameraSessionStats.CAMERA_STATE_OPEN:
                     // Notify the audio subsystem about the facing of the most-recently opened
                     // camera This can be used to select the best audio tuning in case video
                     // recording with that camera will happen.  Since only open events are used, if
@@ -584,13 +659,18 @@
                     AudioManager audioManager = getContext().getSystemService(AudioManager.class);
                     if (audioManager != null) {
                         // Map external to front for audio tuning purposes
-                        String facingStr = (facing == ICameraServiceProxy.CAMERA_FACING_BACK) ?
+                        String facingStr = (facing == CameraSessionStats.CAMERA_FACING_BACK) ?
                                 "back" : "front";
                         String facingParameter = "cameraFacing=" + facingStr;
                         audioManager.setParameters(facingParameter);
                     }
+                    CameraUsageEvent openEvent = new CameraUsageEvent(
+                            cameraId, facing, clientName, apiLevel, isNdk,
+                            FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN,
+                            latencyMs, sessionType);
+                    mCameraUsageHistory.add(openEvent);
                     break;
-                case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
+                case CameraSessionStats.CAMERA_STATE_ACTIVE:
                     // Check current active camera IDs to see if this package is already talking to
                     // some camera
                     boolean alreadyActivePackage = false;
@@ -609,40 +689,55 @@
                     }
 
                     // Update activity events
-                    CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
+                    CameraUsageEvent newEvent = new CameraUsageEvent(
+                            cameraId, facing, clientName, apiLevel, isNdk,
+                            FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__SESSION,
+                            latencyMs, sessionType);
                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
                     if (oldEvent != null) {
                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
-                        oldEvent.markCompleted();
+                        oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
+                                /*resultErrorCount*/0, /*deviceError*/false, streamStats);
                         mCameraUsageHistory.add(oldEvent);
                     }
                     break;
-                case ICameraServiceProxy.CAMERA_STATE_IDLE:
-                case ICameraServiceProxy.CAMERA_STATE_CLOSED:
+                case CameraSessionStats.CAMERA_STATE_IDLE:
+                case CameraSessionStats.CAMERA_STATE_CLOSED:
                     CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
-                    if (doneEvent == null) break;
+                    if (doneEvent != null) {
 
-                    doneEvent.markCompleted();
-                    mCameraUsageHistory.add(doneEvent);
-                    if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
-                        dumpUsageEvents();
-                    }
+                        doneEvent.markCompleted(internalReconfigureCount, requestCount,
+                                resultErrorCount, deviceError, streamStats);
+                        mCameraUsageHistory.add(doneEvent);
 
-                    // Check current active camera IDs to see if this package is still talking to
-                    // some camera
-                    boolean stillActivePackage = false;
-                    for (int i = 0; i < mActiveCameraUsage.size(); i++) {
-                        if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
-                            stillActivePackage = true;
-                            break;
+                        // Check current active camera IDs to see if this package is still
+                        // talking to some camera
+                        boolean stillActivePackage = false;
+                        for (int i = 0; i < mActiveCameraUsage.size(); i++) {
+                            if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
+                                stillActivePackage = true;
+                                break;
+                            }
+                        }
+                        // If not longer active, notify window manager about this package being done
+                        // with camera
+                        if (!stillActivePackage) {
+                            WindowManagerInternal wmi =
+                                    LocalServices.getService(WindowManagerInternal.class);
+                            wmi.removeNonHighRefreshRatePackage(clientName);
                         }
                     }
-                    // If not longer active, notify window manager about this package being done
-                    // with camera
-                    if (!stillActivePackage) {
-                        WindowManagerInternal wmi =
-                                LocalServices.getService(WindowManagerInternal.class);
-                        wmi.removeNonHighRefreshRatePackage(clientName);
+
+                    if (newCameraState == CameraSessionStats.CAMERA_STATE_CLOSED) {
+                        CameraUsageEvent closeEvent = new CameraUsageEvent(
+                                cameraId, facing, clientName, apiLevel, isNdk,
+                                FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE,
+                                latencyMs, sessionType);
+                        mCameraUsageHistory.add(closeEvent);
+                    }
+
+                    if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
+                        dumpUsageEvents();
                     }
 
                     break;
@@ -683,10 +778,10 @@
 
     private static String cameraStateToString(int newCameraState) {
         switch (newCameraState) {
-            case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
-            case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
-            case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
-            case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+            case CameraSessionStats.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+            case CameraSessionStats.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+            case CameraSessionStats.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+            case CameraSessionStats.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
             default: break;
         }
         return "CAMERA_STATE_UNKNOWN";
@@ -694,9 +789,9 @@
 
     private static String cameraFacingToString(int cameraFacing) {
         switch (cameraFacing) {
-            case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
-            case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
-            case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
+            case CameraSessionStats.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
+            case CameraSessionStats.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
+            case CameraSessionStats.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
             default: break;
         }
         return "CAMERA_FACING_UNKNOWN";
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 3091a71..163788f 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -21,7 +21,7 @@
 import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.InetAddresses;
-import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -447,9 +447,10 @@
 
     private LinkAddress getLinkAddress(String iface) {
         try {
-            InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
-            return config.getLinkAddress();
-        } catch (RemoteException | IllegalStateException e) {
+            final InterfaceConfigurationParcel config = mNetd.interfaceGetCfg(iface);
+            return new LinkAddress(
+                    InetAddresses.parseNumericAddress(config.ipv4Addr), config.prefixLength);
+        } catch (IllegalArgumentException | RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error getting link properties: " + e);
             return null;
         }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4d959d0..bdd315d 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -207,7 +207,8 @@
     @VisibleForTesting protected String mPackage;
     private int mOwnerUID;
     private boolean mIsPackageTargetingAtLeastQ;
-    private String mInterface;
+    @VisibleForTesting
+    protected String mInterface;
     private Connection mConnection;
 
     /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 3172a04..d7dcbde 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -100,7 +100,7 @@
     private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
 
     public DeviceStateManagerService(@NonNull Context context) {
-        this(context, new DeviceStatePolicyImpl());
+        this(context, new DeviceStatePolicyImpl(context));
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f2413ed..2ba8758 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -22,10 +22,22 @@
 /**
  * Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and
  * operations.
+ * @hide
  */
 public class DisplayGroup {
 
-    final List<LogicalDisplay> mDisplays = new ArrayList<>();
+    public static final int DEFAULT = 0;
+
+    private final List<LogicalDisplay> mDisplays = new ArrayList<>();
+    private final int mGroupId;
+
+    DisplayGroup(int groupId) {
+        mGroupId = groupId;
+    }
+
+    int getGroupId() {
+        return mGroupId;
+    }
 
     void addDisplay(LogicalDisplay display) {
         if (!mDisplays.contains(display)) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 004e481..4e60f1f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2536,6 +2536,13 @@
         }
 
         @Override
+        public int getDisplayGroupId(int displayId) {
+            synchronized (mSyncRoot) {
+                return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId);
+            }
+        }
+
+        @Override
         public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
             return systemScreenshotInternal(displayId);
         }
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 45c38b4..6b74170 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -94,6 +94,7 @@
     private final SparseArray<LogicalDisplay> mLogicalDisplays =
             new SparseArray<LogicalDisplay>();
     private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
+    private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1;
 
     /** A mapping from logical display id to display group. */
     private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
@@ -178,6 +179,15 @@
         }
     }
 
+    public int getDisplayGroupIdLocked(int displayId) {
+        final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+        if (displayGroup != null) {
+            return displayGroup.getGroupId();
+        }
+
+        return -1;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("LogicalDisplayMapper:");
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
@@ -309,7 +319,8 @@
 
         final DisplayGroup displayGroup;
         if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
-            displayGroup = new DisplayGroup();
+            final int groupId = assignDisplayGroupIdLocked(isDefault);
+            displayGroup = new DisplayGroup(groupId);
         } else {
             displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
         }
@@ -345,7 +356,8 @@
                 if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
                     // The display should have its own DisplayGroup.
                     if (defaultDisplayGroup.removeDisplay(display)) {
-                        final DisplayGroup displayGroup = new DisplayGroup();
+                        final int groupId = assignDisplayGroupIdLocked(false);
+                        final DisplayGroup displayGroup = new DisplayGroup(groupId);
                         displayGroup.addDisplay(display);
                         mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
                     }
@@ -381,6 +393,10 @@
         return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
     }
 
+    private int assignDisplayGroupIdLocked(boolean isDefault) {
+        return isDefault ? DisplayGroup.DEFAULT : mNextNonDefaultGroupId++;
+    }
+
     private int assignLayerStackLocked(int displayId) {
         // Currently layer stacks and display ids are the same.
         // This need not be the case.
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index d0cc8e71..42dcbeb 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -501,15 +501,6 @@
     static final int DISABLED = 0;
     static final int ENABLED = 1;
 
-    @IntDef({
-            VERSION_1_4,
-            VERSION_2_0
-    })
-    @interface CecVersion {}
-    static final int VERSION_1_3 = 0x04;
-    static final int VERSION_1_4 = 0x05;
-    static final int VERSION_2_0 = 0x06;
-
     static final int ALL_DEVICE_TYPES_TV = 7;
     static final int ALL_DEVICE_TYPES_RECORDER = 6;
     static final int ALL_DEVICE_TYPES_TUNER = 5;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index e7f302c..fb71d95 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.input.InputManager;
@@ -560,7 +561,7 @@
     protected abstract List<Integer> getDeviceFeatures();
 
     protected boolean handleGiveFeatures(HdmiCecMessage message) {
-        if (mService.getCecVersion() < Constants.VERSION_2_0) {
+        if (mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 109f6a7..9ca1f91 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -128,25 +128,6 @@
                 String.valueOf(addr));
     }
 
-    @ServiceThreadOnly
-    void queryDisplayStatus(IHdmiControlCallback callback) {
-        assertRunOnServiceThread();
-        List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class);
-        if (!actions.isEmpty()) {
-            Slog.i(TAG, "queryDisplayStatus already in progress");
-            actions.get(0).addCallback(callback);
-            return;
-        }
-        DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV,
-                callback);
-        if (action == null) {
-            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
-            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
-            return;
-        }
-        addAndStartAction(action);
-    }
-
     @Override
     @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 62b7d8f..3b3ac74 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -70,6 +70,25 @@
         super(service, deviceType);
     }
 
+    @ServiceThreadOnly
+    void queryDisplayStatus(IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class);
+        if (!actions.isEmpty()) {
+            Slog.i(TAG, "queryDisplayStatus already in progress");
+            actions.get(0).addCallback(callback);
+            return;
+        }
+        DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV,
+                callback);
+        if (action == null) {
+            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
+            return;
+        }
+        addAndStartAction(action);
+    }
+
     @Override
     @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
@@ -87,10 +106,14 @@
     @ServiceThreadOnly
     protected void sendStandby(int deviceId) {
         assertRunOnServiceThread();
-
-        // Send standby to TV only for now
-        int targetAddress = Constants.ADDR_TV;
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress));
+        String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+        if (sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+            return;
+        }
+        mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
     }
 
     @ServiceThreadOnly
@@ -113,6 +136,44 @@
     }
 
     @ServiceThreadOnly
+    void toggleAndFollowTvPower() {
+        assertRunOnServiceThread();
+        // Wake up Android framework to take over CEC control from the microprocessor.
+        mService.wakeUp();
+        mService.queryDisplayStatus(new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int status) {
+                if (status == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+                    Slog.i(TAG, "TV power toggle: TV power status unknown");
+                    sendUserControlPressedAndReleased(Constants.ADDR_TV,
+                            HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+                    // Source device remains awake.
+                } else if (status == HdmiControlManager.POWER_STATUS_ON
+                        || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
+                    Slog.i(TAG, "TV power toggle: turning off TV");
+                    sendStandby(0 /*unused */);
+                    // Source device goes to standby, to follow the toggled TV power state.
+                    mService.standby();
+                } else if (status == HdmiControlManager.POWER_STATUS_STANDBY
+                        || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
+                    Slog.i(TAG, "TV power toggle: turning on TV");
+                    oneTouchPlay(new IHdmiControlCallback.Stub() {
+                        @Override
+                        public void onComplete(int result) {
+                            if (result != HdmiControlManager.RESULT_SUCCESS) {
+                                Slog.w(TAG, "Failed to complete One Touch Play. result=" + result);
+                                sendUserControlPressedAndReleased(Constants.ADDR_TV,
+                                        HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+                            }
+                        }
+                    });
+                    // Source device remains awake, to follow the toggled TV power state.
+                }
+            }
+        });
+    }
+
+    @ServiceThreadOnly
     protected void onActiveSourceLost() {
         // Nothing to do.
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 1a481b6..96303ce 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -16,10 +16,10 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 
 import com.android.server.hdmi.Constants.AudioCodec;
-import com.android.server.hdmi.Constants.CecVersion;
 
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
@@ -696,9 +696,9 @@
         return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES);
     }
 
-    static HdmiCecMessage buildReportFeatures(int src, @CecVersion int cecVersion,
-            List<Integer> allDeviceTypes, int rcProfile, List<Integer> rcFeatures,
-            List<Integer> deviceFeatures) {
+    static HdmiCecMessage buildReportFeatures(int src,
+            @HdmiControlManager.HdmiCecVersion int cecVersion, List<Integer> allDeviceTypes,
+            int rcProfile, List<Integer> rcFeatures, List<Integer> deviceFeatures) {
         byte cecVersionByte = (byte) (cecVersion & 0xFF);
         byte deviceTypes = 0;
         for (Integer deviceType : allDeviceTypes) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index baed9cc..d2a2d72 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -201,7 +201,7 @@
         addValidationInfo(
                 Constants.MESSAGE_REPORT_POWER_STATUS,
                 new OneByteRangeValidator(0x00, 0x03),
-                DEST_DIRECT);
+                DEST_DIRECT | DEST_BROADCAST);
 
         // Messages for the General Protocol.
         addValidationInfo(Constants.MESSAGE_FEATURE_ABORT,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java
new file mode 100644
index 0000000..c4dadaa
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.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 com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+import android.hardware.hdmi.HdmiControlManager;
+
+/**
+ * Storage of HDMI-CEC power status and controls possible cases where power status changes must also
+ * broadcast {@code <Report Power Status>} messages.
+ *
+ * All HDMI-CEC related power status changes should be done through this class.
+ */
+class HdmiCecPowerStatusController {
+
+    private final HdmiControlService mHdmiControlService;
+
+    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+
+    HdmiCecPowerStatusController(HdmiControlService hdmiControlService) {
+        mHdmiControlService = hdmiControlService;
+    }
+
+    int getPowerStatus() {
+        return mPowerStatus;
+    }
+
+    boolean isPowerStatusOn() {
+        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON;
+    }
+
+    boolean isPowerStatusStandby() {
+        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
+    }
+
+    boolean isPowerStatusTransientToOn() {
+        return mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+    }
+
+    boolean isPowerStatusTransientToStandby() {
+        return mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+    }
+
+    @ServiceThreadOnly
+    void setPowerStatus(int powerStatus) {
+        setPowerStatus(powerStatus, true);
+    }
+
+    @ServiceThreadOnly
+    void setPowerStatus(int powerStatus, boolean sendPowerStatusUpdate) {
+        if (powerStatus == mPowerStatus) {
+            return;
+        }
+
+        mPowerStatus = powerStatus;
+        if (sendPowerStatusUpdate
+                && mHdmiControlService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+            sendReportPowerStatus(mPowerStatus);
+        }
+    }
+
+    private void sendReportPowerStatus(int powerStatus) {
+        for (HdmiCecLocalDevice localDevice : mHdmiControlService.getAllLocalDevices()) {
+            mHdmiControlService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildReportPowerStatus(localDevice.mAddress,
+                            Constants.ADDR_BROADCAST, powerStatus));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index dcd6c02..01ec3b4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -350,8 +350,8 @@
 
     private HdmiCecMessageValidator mMessageValidator;
 
-    @ServiceThreadOnly
-    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+    private final HdmiCecPowerStatusController mPowerStatusController =
+            new HdmiCecPowerStatusController(this);
 
     @ServiceThreadOnly
     private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault());
@@ -530,7 +530,8 @@
             mIoThread.start();
             mIoLooper = mIoThread.getLooper();
         }
-        mPowerStatus = getInitialPowerStatus();
+
+        mPowerStatusController.setPowerStatus(getInitialPowerStatus());
         mProhibitMode = false;
         mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
         mHdmiCecVolumeControlEnabled = readBooleanSetting(
@@ -673,10 +674,12 @@
      * Updates the power status once the initialization of local devices is complete.
      */
     private void updatePowerStatusOnInitializeCecComplete() {
-        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
-            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
-        } else if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
-            mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+        if (mPowerStatusController.isPowerStatusTransientToOn()) {
+            mHandler.post(() -> mPowerStatusController.setPowerStatus(
+                    HdmiControlManager.POWER_STATUS_ON));
+        } else if (mPowerStatusController.isPowerStatusTransientToStandby()) {
+            mHandler.post(() -> mPowerStatusController.setPowerStatus(
+                    HdmiControlManager.POWER_STATUS_STANDBY));
         }
     }
 
@@ -1686,7 +1689,7 @@
         public void oneTouchPlay(final IHdmiControlCallback callback) {
             enforceAccessPermission();
             int pid = Binder.getCallingPid();
-            Slog.d(TAG, "Proccess pid: " + pid + " is calling oneTouchPlay.");
+            Slog.d(TAG, "Process pid: " + pid + " is calling oneTouchPlay.");
             runOnServiceThread(new Runnable() {
                 @Override
                 public void run() {
@@ -1696,6 +1699,19 @@
         }
 
         @Override
+        public void toggleAndFollowTvPower() {
+            enforceAccessPermission();
+            int pid = Binder.getCallingPid();
+            Slog.d(TAG, "Process pid: " + pid + " is calling toggleAndFollowTvPower.");
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiControlService.this.toggleAndFollowTvPower();
+                }
+            });
+        }
+
+        @Override
         public void queryDisplayStatus(final IHdmiControlCallback callback) {
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
@@ -2191,7 +2207,7 @@
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
             pw.println("mProhibitMode: " + mProhibitMode);
-            pw.println("mPowerStatus: " + mPowerStatus);
+            pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
 
             // System settings
             pw.println("System_settings:");
@@ -2362,7 +2378,23 @@
     }
 
     @ServiceThreadOnly
-    private void queryDisplayStatus(final IHdmiControlCallback callback) {
+    @VisibleForTesting
+    protected void toggleAndFollowTvPower() {
+        assertRunOnServiceThread();
+        HdmiCecLocalDeviceSource source = playback();
+        if (source == null) {
+            source = audioSystem();
+        }
+
+        if (source == null) {
+            Slog.w(TAG, "Local source device not available");
+            return;
+        }
+        source.toggleAndFollowTvPower();
+    }
+
+    @ServiceThreadOnly
+    protected void queryDisplayStatus(final IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (!mAddressAllocated) {
             mDisplayStatusCallback = callback;
@@ -2371,9 +2403,13 @@
             return;
         }
 
-        HdmiCecLocalDevicePlayback source = playback();
+        HdmiCecLocalDeviceSource source = playback();
         if (source == null) {
-            Slog.w(TAG, "Local playback device not available");
+            source = audioSystem();
+        }
+
+        if (source == null) {
+            Slog.w(TAG, "Local source device not available");
             invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
             return;
         }
@@ -2831,34 +2867,34 @@
     @ServiceThreadOnly
     int getPowerStatus() {
         assertRunOnServiceThread();
-        return mPowerStatus;
+        return mPowerStatusController.getPowerStatus();
     }
 
     @ServiceThreadOnly
     @VisibleForTesting
     void setPowerStatus(int powerStatus) {
         assertRunOnServiceThread();
-        mPowerStatus = powerStatus;
+        mPowerStatusController.setPowerStatus(powerStatus);
     }
 
     @ServiceThreadOnly
     boolean isPowerOnOrTransient() {
         assertRunOnServiceThread();
-        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
-                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+        return mPowerStatusController.isPowerStatusOn()
+                || mPowerStatusController.isPowerStatusTransientToOn();
     }
 
     @ServiceThreadOnly
     boolean isPowerStandbyOrTransient() {
         assertRunOnServiceThread();
-        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
-                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+        return mPowerStatusController.isPowerStatusStandby()
+                || mPowerStatusController.isPowerStatusTransientToStandby();
     }
 
     @ServiceThreadOnly
     boolean isPowerStandby() {
         assertRunOnServiceThread();
-        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
+        return mPowerStatusController.isPowerStatusStandby();
     }
 
     @ServiceThreadOnly
@@ -2895,7 +2931,8 @@
     @ServiceThreadOnly
     private void onWakeUp(@WakeReason final int wakeUpAction) {
         assertRunOnServiceThread();
-        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+        mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
+                false);
         if (mCecController != null) {
             if (mHdmiControlEnabled) {
                 int startReason = -1;
@@ -2926,14 +2963,15 @@
     @VisibleForTesting
     protected void onStandby(final int standbyAction) {
         assertRunOnServiceThread();
-        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+        mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
+                false);
         invokeVendorCommandListenersOnControlStateChanged(false,
                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
 
         final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
 
         if (!isStandbyMessageReceived() && !canGoToStandby()) {
-            mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+            mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
             for (HdmiCecLocalDevice device : devices) {
                 device.onStandby(mStandbyMessageReceived, standbyAction);
             }
@@ -3013,10 +3051,10 @@
         assertRunOnServiceThread();
         Slog.v(TAG, "onStandbyCompleted");
 
-        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
+        if (!mPowerStatusController.isPowerStatusTransientToStandby()) {
             return;
         }
-        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+        mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
         for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
             device.onStandby(mStandbyMessageReceived, standbyAction);
         }
diff --git a/services/core/java/com/android/server/hdmi/cec_key_handling.md b/services/core/java/com/android/server/hdmi/cec_key_handling.md
index d150dd3..1b41a67 100644
--- a/services/core/java/com/android/server/hdmi/cec_key_handling.md
+++ b/services/core/java/com/android/server/hdmi/cec_key_handling.md
@@ -9,13 +9,13 @@
 
 The general action for key handling is described in the table
 
-| Android Key | TV Panel                             | OTT                                  | Soundbar                          |
-| ----------- | -----------------                    | -------------------                  | -------------------               |
-| general     | Send to active source                | handle on device                     | handle on device                  |
-| POWER       | Toggle the device power state        | Toggle the TV power state            | Toggle the TV power state         |
-| TV_POWER    | Toggle the device power state        | Toggle the TV power state            | Toggle the TV power state         |
-| HOME        | Turn on TV, Set active Source to TV, go to home screen | OTP, and go to home screen | OTP, and go to home screen |
-| volume keys | Handle on device or send to soundbar | Send to TV or soundbar               | Handle on device or send to TV    |
+| Android Key | TV Panel                                               | OTT                        | Soundbar                                               |
+| ----------- | -----------------                                      | -------------------        | -------------------                                    |
+| general     | Send to active source                                  | handle on device           | handle on device                                       |
+| POWER       | Toggle the device power state  | Toggle the OTT power state, TV power state follows | Toggle the soundbar power state, TV power state follows|
+| TV_POWER    | Toggle the device power state  | Toggle the TV power state, OTT power state follows | Toggle the TV power state, soundbar power state follows|
+| HOME        | Turn on TV, Set active Source to TV, go to home screen | OTP, and go to home screen | OTP, and go to home screen                             |
+| volume keys | Handle on device or send to soundbar                   | Send to TV or soundbar     | Handle on device or send to TV                         |
 
 Special cases and flags for each key are described below
 
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 7fc3d2b..c4c0f68 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -31,7 +31,6 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -43,7 +42,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c516232..cfb5116 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -111,7 +111,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
@@ -184,6 +183,7 @@
 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 77e2fbd2..0e908d4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -30,7 +30,6 @@
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -46,6 +45,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.textservices.TextServicesManagerInternal;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 8c1afab0..7d3ccbf 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -16,9 +16,10 @@
 
 package com.android.server.location;
 
-import android.os.BasicShellCommandHandler;
 import android.os.UserHandle;
 
+import com.android.modules.utils.BasicShellCommandHandler;
+
 import java.io.PrintWriter;
 import java.util.Objects;
 
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 4a85342..5a90fa7 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -322,12 +322,28 @@
 
     @Override
     protected boolean isActive(GeofenceRegistration registration) {
-        CallerIdentity identity = registration.getIdentity();
-        return registration.isPermitted()
-                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
-                && mSettingsHelper.isLocationEnabled(identity.getUserId())
-                && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName());
+        return registration.isPermitted() && isActive(registration.getIdentity());
+    }
+
+    private boolean isActive(CallerIdentity identity) {
+        if (identity.isSystemServer()) {
+            if (!mSettingsHelper.isLocationEnabled(mUserInfoHelper.getCurrentUserId())) {
+                return false;
+            }
+        } else {
+            if (!mSettingsHelper.isLocationEnabled(identity.getUserId())) {
+                return false;
+            }
+            if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+                return false;
+            }
+            if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                    identity.getPackageName())) {
+                return false;
+            }
+        }
+
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index e68f595..7e848e0 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -266,11 +266,30 @@
         CallerIdentity identity = registration.getIdentity();
         return registration.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
-                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
-                && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
-                identity.getUserId())
-                && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName());
+                && isActive(identity);
+    }
+
+    private boolean isActive(CallerIdentity identity) {
+        if (identity.isSystemServer()) {
+            if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
+                    mUserInfoHelper.getCurrentUserId())) {
+                return false;
+            }
+        } else {
+            if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
+                    identity.getUserId())) {
+                return false;
+            }
+            if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+                return false;
+            }
+            if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                    identity.getPackageName())) {
+                return false;
+            }
+        }
+
+        return true;
     }
 
     private boolean isBackgroundRestrictionExempt(CallerIdentity identity) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index ceac10d..a8889fd 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -709,8 +709,10 @@
                         "GNSS HAL Requesting location updates from %s provider for %d millis.",
                         provider, durationMillis));
 
-        locationManager.requestLocationUpdates(provider, locationRequest.build(),
-                DIRECT_EXECUTOR, locationListener);
+        if (locationManager.getProvider(provider) != null) {
+            locationManager.requestLocationUpdates(provider, locationRequest.build(),
+                    DIRECT_EXECUTOR, locationListener);
+        }
     }
 
     private void injectBestLocation(Location location) {
diff --git a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
index 56dd92a..d4a8fbd 100644
--- a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
@@ -88,11 +88,6 @@
         return mUserManager;
     }
 
-    /**
-     * Returns an array of running user ids. This will include all running users, and will also
-     * include any profiles of the running users. The caller must never mutate the returned
-     * array.
-     */
     @Override
     public int[] getRunningUserIds() {
         IActivityManager activityManager = getActivityManager();
@@ -110,10 +105,6 @@
         }
     }
 
-    /**
-     * Returns true if the given user id is either the current user or a profile of the current
-     * user.
-     */
     @Override
     public boolean isCurrentUserId(@UserIdInt int userId) {
         ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
@@ -130,6 +121,21 @@
     }
 
     @Override
+    public @UserIdInt int getCurrentUserId() {
+        ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
+        if (activityManagerInternal != null) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return activityManagerInternal.getCurrentUserId();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    @Override
     protected int[] getProfileIds(@UserIdInt int userId) {
         UserManager userManager = getUserManager();
 
diff --git a/services/core/java/com/android/server/location/injector/UserInfoHelper.java b/services/core/java/com/android/server/location/injector/UserInfoHelper.java
index 3b7e3b40..0fcc1ec 100644
--- a/services/core/java/com/android/server/location/injector/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/injector/UserInfoHelper.java
@@ -132,6 +132,12 @@
      */
     public abstract boolean isCurrentUserId(@UserIdInt int userId);
 
+    /**
+     * Returns the current user id. Where possible, prefer to use {@link #isCurrentUserId(int)}
+     * instead, as that method has more flexibility.
+     */
+    public abstract @UserIdInt int getCurrentUserId();
+
     protected abstract int[] getProfileIds(@UserIdInt int userId);
 
     /**
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index eca6070..c5d7f2c 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1469,18 +1469,9 @@
 
     public @Nullable Location getLastLocation(CallerIdentity identity,
             @PermissionLevel int permissionLevel, boolean ignoreLocationSettings) {
-        if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName())) {
+        if (!isActive(ignoreLocationSettings, identity)) {
             return null;
         }
-        if (!ignoreLocationSettings) {
-            if (!isEnabled(identity.getUserId())) {
-                return null;
-            }
-            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
-                return null;
-            }
-        }
 
         // lastly - note app ops
         if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
@@ -1905,20 +1896,16 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        CallerIdentity identity = registration.getIdentity();
-
         if (!registration.isPermitted()) {
             return false;
         }
 
-        if (!registration.getRequest().isLocationSettingsIgnored()) {
-            if (!isEnabled(identity.getUserId())) {
-                return false;
-            }
-            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
-                return false;
-            }
+        boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
+        if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
+            return false;
+        }
 
+        if (!locationSettingsIgnored) {
             switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
                 case LOCATION_MODE_FOREGROUND_ONLY:
                     if (!registration.isForeground()) {
@@ -1944,8 +1931,32 @@
             }
         }
 
-        return !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName());
+        return true;
+    }
+
+    private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
+        if (identity.isSystemServer()) {
+            if (!locationSettingsIgnored) {
+                if (!isEnabled(mUserHelper.getCurrentUserId())) {
+                    return false;
+                }
+            }
+        } else {
+            if (!locationSettingsIgnored) {
+                if (!isEnabled(identity.getUserId())) {
+                    return false;
+                }
+                if (!mUserHelper.isCurrentUserId(identity.getUserId())) {
+                    return false;
+                }
+            }
+            if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                    identity.getPackageName())) {
+                return false;
+            }
+        }
+
+        return true;
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ad96e76..f7f3440 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -83,7 +83,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -130,6 +129,7 @@
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.util.HexEncoding;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
index 285e722..9857fb6 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -16,24 +16,39 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.security.keystore2.AndroidKeyStoreProvider;
+
 import java.io.IOException;
-import java.security.cert.CertificateException;
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 
 /**
  * Implementation of {@link KeyStoreProxy} that delegates all method calls to the {@link KeyStore}.
  */
 public class KeyStoreProxyImpl implements KeyStoreProxy {
 
-    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private final KeyStore mKeyStore;
 
     /**
+     * TODO This function redirects keystore access to the legacy keystore during a transitional
+     *      phase during which not all calling code has been adjusted to use Keystore 2.0.
+     *      This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete.
+     *      The specific bug for this component is b/171305545.
+     */
+    static String androidKeystoreProviderName() {
+        if (AndroidKeyStoreProvider.isInstalled()) {
+            return "AndroidKeyStoreLegacy";
+        } else {
+            return "AndroidKeyStore";
+        }
+
+    }
+
+    /**
      * A new instance, delegating to {@code keyStore}.
      */
     public KeyStoreProxyImpl(KeyStore keyStore) {
@@ -69,7 +84,7 @@
      * @throws KeyStoreException if there was a problem getting or initializing the key store.
      */
     public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
-        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+        KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
         try {
             keyStore.load(/*param=*/ null);
         } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index cb5ca85b..dff1df7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -86,8 +86,6 @@
     private final KeyStoreProxy mKeyStore;
     private final RecoverableKeyStoreDb mDatabase;
 
-    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
-
     /**
      * A new instance operating on behalf of {@code userId}, storing its prefs in the location
      * defined by {@code context}.
@@ -472,7 +470,7 @@
      * @throws KeyStoreException if there was a problem getting or initializing the key store.
      */
     private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
-        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+        KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName());
         try {
             keyStore.load(/*param=*/ null);
         } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dfeb682..b1289dd 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7164,6 +7164,16 @@
             return true;
         }
 
+        // Suppressed since it's a non-interruptive update to a bubble-suppressed notification
+        final boolean isBubbleOrOverflowed = record.canBubble() && (record.isFlagBubbleRemoved()
+                || record.getNotification().isBubbleNotification());
+        if (record.isUpdate && !record.isInterruptive() && isBubbleOrOverflowed
+                && record.getNotification().getBubbleMetadata() != null) {
+            if (record.getNotification().getBubbleMetadata().isNotificationSuppressed()) {
+                return true;
+            }
+        }
+
         return false;
     }
 
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 6e82c24..f19d353 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -28,14 +28,14 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.service.oemlock.IOemLockService;
 import android.util.Slog;
 
 import com.android.server.LocalServices;
 import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.pm.UserRestrictionsUtils;
 
 /**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index dd33865..c4a23f9 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -73,7 +73,6 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Pair;
@@ -775,6 +774,9 @@
                 throw new IllegalArgumentException(
                         "To query by locus ID, package name must also be set");
             }
+            if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+                ensureStrictAccessShortcutsPermission(callingPackage);
+            }
 
             // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
             return new ParceledListSlice<>((List<ShortcutInfo>)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 4a799b5..4cee2e5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -517,7 +517,7 @@
             String installerAttributionTag, int userId)
             throws IOException {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(
+        mPm.enforceCrossUserPermission(
                 callingUid, userId, true, true, "createSession");
 
         if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -912,7 +912,7 @@
     @Override
     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(
+        mPm.enforceCrossUserPermission(
                 callingUid, userId, true, false, "getAllSessions");
 
         final List<SessionInfo> result = new ArrayList<>();
@@ -930,7 +930,7 @@
 
     @Override
     public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
-        mPermissionManager.enforceCrossUserPermission(
+        mPm.enforceCrossUserPermission(
                 Binder.getCallingUid(), userId, true, false, "getMySessions");
         mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
 
@@ -954,7 +954,7 @@
     public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
                 IntentSender statusReceiver, int userId) {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
             mAppOps.checkPackage(callingUid, callerPackageName);
         }
@@ -1006,7 +1006,7 @@
             String callerPackageName, IntentSender statusReceiver, int userId) {
         final int callingUid = Binder.getCallingUid();
         mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
             mAppOps.checkPackage(callingUid, callerPackageName);
         }
@@ -1037,7 +1037,7 @@
 
     @Override
     public void registerCallback(IPackageInstallerCallback callback, int userId) {
-        mPermissionManager.enforceCrossUserPermission(
+        mPm.enforceCrossUserPermission(
                 Binder.getCallingUid(), userId, true, false, "registerCallback");
         registerCallback(callback, eventUserId -> userId == eventUserId);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a03c405c..3880711 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -94,6 +94,11 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
+import static android.content.pm.PackageManager.TYPE_ACTIVITY;
+import static android.content.pm.PackageManager.TYPE_PROVIDER;
+import static android.content.pm.PackageManager.TYPE_RECEIVER;
+import static android.content.pm.PackageManager.TYPE_SERVICE;
+import static android.content.pm.PackageManager.TYPE_UNKNOWN;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
 import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
@@ -103,7 +108,6 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
@@ -160,6 +164,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
+import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
@@ -195,8 +200,11 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ComponentType;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageManager.ModuleInfoFlags;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.PackageManager.PropertyLocation;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
@@ -245,7 +253,6 @@
 import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -274,7 +281,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.incremental.IStorageHealthListener;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
@@ -339,10 +345,8 @@
 import com.android.internal.os.Zygote;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -397,7 +401,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -575,21 +578,6 @@
 
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
-    private static final int TYPE_UNKNOWN = 0;
-    private static final int TYPE_ACTIVITY = 1;
-    private static final int TYPE_RECEIVER = 2;
-    private static final int TYPE_SERVICE = 3;
-    private static final int TYPE_PROVIDER = 4;
-    @IntDef(prefix = { "TYPE_" }, value = {
-            TYPE_UNKNOWN,
-            TYPE_ACTIVITY,
-            TYPE_RECEIVER,
-            TYPE_SERVICE,
-            TYPE_PROVIDER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ComponentType {}
-
     /**
      * Timeout (in milliseconds) after which the watchdog should declare that
      * our handler thread is wedged.  The usual default for such things is one
@@ -1320,6 +1308,8 @@
 
     private final IncrementalManager mIncrementalManager;
 
+    private final PackageProperty mPackageProperty = new PackageProperty();
+
     private static class IFVerificationParams {
         String packageName;
         boolean hasDomainUrls;
@@ -3842,7 +3832,7 @@
         // are all flushed.  Not really needed, but keeps things nice and
         // tidy.
         t.traceBegin("GC");
-        Runtime.getRuntime().gc();
+        VMRuntime.getRuntime().requestConcurrentGC();
         t.traceEnd();
 
         // The initial scanning above does many calls into installd while
@@ -4632,8 +4622,7 @@
         if (!mUserManager.exists(userId)) {
             throw new SecurityException("User doesn't exist");
         }
-        mPermissionManager.enforceCrossUserPermission(
-                callingUid, userId, false, false, "checkPackageStartable");
+        enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable");
         final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -4664,8 +4653,8 @@
     public boolean isPackageAvailable(String packageName, int userId) {
         if (!mUserManager.exists(userId)) return false;
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/, "is package available");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "is package available");
         synchronized (mLock) {
             AndroidPackage p = mPackages.get(packageName);
             if (p != null) {
@@ -4707,7 +4696,7 @@
             int flags, int filterCallingUid, int userId) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForPackage(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get package info");
 
         // reader
@@ -5009,8 +4998,8 @@
         if (!mUserManager.exists(userId)) return -1;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "getPackageUid");
         return getPackageUidInternal(packageName, flags, userId, callingUid);
     }
 
@@ -5042,8 +5031,8 @@
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "getPackageGids");
 
         // reader
         synchronized (mLock) {
@@ -5126,7 +5115,7 @@
         flags = updateFlagsForApplication(flags, userId);
 
         if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
-            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+            enforceCrossUserPermission(Binder.getCallingUid(), userId,
                     false /* requireFullPermission */, false /* checkShell */,
                     "get application info");
         }
@@ -5430,8 +5419,7 @@
         if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
             // require the permission to be held; the calling uid and given user id referring
             // to the same user is not sufficient
-            mPermissionManager.enforceCrossUserPermission(
-                    Binder.getCallingUid(), userId, false, false,
+            enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
                     !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
                     "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
                     + Debug.getCallers(5));
@@ -5553,7 +5541,7 @@
         flags = updateFlagsForComponent(flags, userId);
 
         if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
-            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+            enforceCrossUserPermission(Binder.getCallingUid(), userId,
                     false /* requireFullPermission */, false /* checkShell */, "get activity info");
         }
 
@@ -5635,8 +5623,8 @@
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /* requireFullPermission */, false /* checkShell */, "get receiver info");
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get receiver info");
         synchronized (mLock) {
             ParsedActivity a = mComponentResolver.getReceiver(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
@@ -5744,9 +5732,8 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
                 "getDeclaredSharedLibraries");
         int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getDeclaredSharedLibraries");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "getDeclaredSharedLibraries");
 
         Preconditions.checkNotNull(packageName, "packageName cannot be null");
         Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
@@ -5860,9 +5847,8 @@
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
-        mPermissionManager.enforceCrossUserOrProfilePermission(
-                callingUid, userId, false /* requireFullPermission */, false /* checkShell */,
-                "get service info");
+        enforceCrossUserOrProfilePermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get service info");
         synchronized (mLock) {
             ParsedService s = mComponentResolver.getService(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
@@ -5891,8 +5877,8 @@
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /* requireFullPermission */, false /* checkShell */, "get provider info");
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get provider info");
         synchronized (mLock) {
             ParsedProvider p = mComponentResolver.getProvider(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
@@ -6029,8 +6015,7 @@
         if (!mUserManager.exists(userId)) {
             return null;
         }
-        mPermissionManager.enforceCrossUserPermission(
-                callingUid, userId, false, false, "getChangedPackages");
+        enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
         synchronized (mLock) {
             if (sequenceNumber >= mChangedPackagesSequenceNumber) {
                 return null;
@@ -6655,8 +6640,8 @@
             flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
                     isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
                             flags));
-            mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                    false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
+            enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                    false /*checkShell*/, "resolve intent");
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
             final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
@@ -7363,7 +7348,7 @@
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */,
                 "query intent activities");
         final String pkgName = intent.getPackage();
@@ -8170,9 +8155,8 @@
         flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
                         flags));
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/,
-                "query intent activity options");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "query intent activity options");
         final String resultsAction = intent.getAction();
 
         final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
@@ -8351,9 +8335,8 @@
             String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/,
-                "query intent receivers");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
         flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
@@ -8473,7 +8456,7 @@
             String resolvedType, int flags, int userId, int callingUid,
             boolean includeInstantApps) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
-        mPermissionManager.enforceCrossUserOrProfilePermission(callingUid,
+        enforceCrossUserOrProfilePermission(callingUid,
                 userId,
                 false /*requireFullPermission*/,
                 false /*checkShell*/,
@@ -8757,9 +8740,8 @@
         final boolean listApex = (flags & MATCH_APEX) != 0;
         final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
 
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /* requireFullPermission */, false /* checkShell */,
-                "get installed packages");
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get installed packages");
 
         // writer
         synchronized (mLock) {
@@ -8869,9 +8851,8 @@
             String[] permissions, int flags, int userId) {
         if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForPackage(flags, userId);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "get packages holding permissions");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                false /* checkShell */, "get packages holding permissions");
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
 
         // writer
@@ -8913,7 +8894,7 @@
         flags = updateFlagsForApplication(flags, userId);
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
 
-        mPermissionManager.enforceCrossUserPermission(
+        enforceCrossUserPermission(
             callingUid,
             userId,
             false /* requireFullPermission */,
@@ -8987,9 +8968,8 @@
             mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                     "getEphemeralApplications");
         }
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getEphemeralApplications");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                false /* checkShell */, "getEphemeralApplications");
         synchronized (mLock) {
             List<InstantAppInfo> instantApps = mInstantAppRegistry
                     .getInstantAppsLPr(userId);
@@ -9003,9 +8983,8 @@
     @Override
     public boolean isInstantApp(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "isInstantApp");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "isInstantApp");
 
         return isInstantAppInternal(packageName, userId, callingUid);
     }
@@ -9039,9 +9018,8 @@
             return null;
         }
 
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getInstantAppCookie");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                false /* checkShell */, "getInstantAppCookie");
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return null;
         }
@@ -9057,9 +9035,8 @@
             return true;
         }
 
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "setInstantAppCookie");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                true /* checkShell */, "setInstantAppCookie");
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return false;
         }
@@ -9079,9 +9056,8 @@
             mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                     "getInstantAppIcon");
         }
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getInstantAppIcon");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                false /* checkShell */, "getInstantAppIcon");
 
         synchronized (mLock) {
             return mInstantAppRegistry.getInstantAppIconLPw(
@@ -9157,8 +9133,7 @@
             }
         }
         if (!checkedGrants) {
-            mPermissionManager.enforceCrossUserPermission(
-                    callingUid, userId, false, false, "resolveContentProvider");
+            enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider");
         }
         if (providerInfo == null) {
             return null;
@@ -9843,6 +9818,171 @@
         }
     }
 
+    /**
+     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+     *
+     * @param checkShell whether to prevent shell from access if there's a debugging restriction
+     * @param message the message to log on security exception
+     */
+    void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+            boolean requireFullPermission, boolean checkShell, String message) {
+        enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
+                message);
+    }
+
+    /**
+     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+     *
+     * @param checkShell whether to prevent shell from access if there's a debugging restriction
+     * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
+     *                                      permission to be held even if the callingUid and userId
+     *                                      reference the same user.
+     * @param message the message to log on security exception
+     */
+    private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+            boolean requireFullPermission, boolean checkShell,
+            boolean requirePermissionWhenSameUser, String message) {
+        if (userId < 0) {
+            throw new IllegalArgumentException("Invalid userId " + userId);
+        }
+        if (checkShell) {
+            PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+        }
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (hasCrossUserPermission(
+                callingUid, callingUserId, userId, requireFullPermission,
+                requirePermissionWhenSameUser)) {
+            return;
+        }
+        String errorMessage = buildInvalidCrossUserPermissionMessage(
+                callingUid, userId, message, requireFullPermission);
+        Slog.w(TAG, errorMessage);
+        throw new SecurityException(errorMessage);
+    }
+
+    /**
+     * Checks if the request is from the system or an app that has the appropriate cross-user
+     * permissions defined as follows:
+     * <ul>
+     * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+     * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group
+     * to the caller.</li>
+     * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile
+     * group as the caller.</li>
+     * </ul>
+     *
+     * @param checkShell whether to prevent shell from access if there's a debugging restriction
+     * @param message the message to log on security exception
+     */
+    private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+            boolean requireFullPermission, boolean checkShell, String message) {
+        if (userId < 0) {
+            throw new IllegalArgumentException("Invalid userId " + userId);
+        }
+        if (checkShell) {
+            PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+        }
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+                /*requirePermissionWhenSameUser= */ false)) {
+            return;
+        }
+        final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+        if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+                mContext,
+                android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+                PermissionChecker.PID_UNKNOWN,
+                callingUid,
+                mPmInternal.getPackage(callingUid).getPackageName())
+                == PermissionChecker.PERMISSION_GRANTED) {
+            return;
+        }
+        String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+                callingUid, userId, message, requireFullPermission, isSameProfileGroup);
+        Slog.w(TAG, errorMessage);
+        throw new SecurityException(errorMessage);
+    }
+
+    private boolean hasCrossUserPermission(
+            int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+            boolean requirePermissionWhenSameUser) {
+        if (!requirePermissionWhenSameUser && userId == callingUserId) {
+            return true;
+        }
+        if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
+            return true;
+        }
+        if (requireFullPermission) {
+            return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        }
+        return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+    }
+
+    private boolean hasPermission(String permission) {
+        return mContext.checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private static String buildInvalidCrossUserPermissionMessage(int callingUid,
+            @UserIdInt int userId, String message, boolean requireFullPermission) {
+        StringBuilder builder = new StringBuilder();
+        if (message != null) {
+            builder.append(message);
+            builder.append(": ");
+        }
+        builder.append("UID ");
+        builder.append(callingUid);
+        builder.append(" requires ");
+        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        if (!requireFullPermission) {
+            builder.append(" or ");
+            builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+        }
+        builder.append(" to access user ");
+        builder.append(userId);
+        builder.append(".");
+        return builder.toString();
+    }
+
+    private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
+            @UserIdInt int userId, String message, boolean requireFullPermission,
+            boolean isSameProfileGroup) {
+        StringBuilder builder = new StringBuilder();
+        if (message != null) {
+            builder.append(message);
+            builder.append(": ");
+        }
+        builder.append("UID ");
+        builder.append(callingUid);
+        builder.append(" requires ");
+        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        if (!requireFullPermission) {
+            builder.append(" or ");
+            builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+            if (isSameProfileGroup) {
+                builder.append(" or ");
+                builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+            }
+        }
+        builder.append(" to access user ");
+        builder.append(".");
+        return builder.toString();
+    }
+
     @Override
     public void performFstrimIfNeeded() {
         enforceSystemOrRoot("Only the system can request fstrim");
@@ -11442,22 +11582,6 @@
         }
         pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
 
-        if (!pkg.getAdoptPermissions().isEmpty()) {
-            // This package wants to adopt ownership of permissions from
-            // another package.
-            for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) {
-                final String origName = pkg.getAdoptPermissions().get(i);
-                final PackageSetting orig = mSettings.getPackageLPr(origName);
-                if (orig != null) {
-                    if (verifyPackageUpdateLPr(orig, pkg)) {
-                        Slog.i(TAG, "Adopting permissions from " + origName + " to "
-                                + pkg.getPackageName());
-                        mPermissionManager.transferPermissions(origName, pkg.getPackageName());
-                    }
-                }
-            }
-        }
-
         if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
             for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                 final String codePathString = changedAbiCodePath.get(i);
@@ -12577,6 +12701,37 @@
         return true;
     }
 
+    @Override
+    public Property getProperty(String propertyName, String packageName, String className) {
+        Objects.requireNonNull(propertyName);
+        Objects.requireNonNull(packageName);
+        synchronized (mLock) {
+            final PackageSetting ps = getPackageSetting(packageName);
+            if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
+                    UserHandle.getCallingUserId())) {
+                return null;
+            }
+            return mPackageProperty.getProperty(propertyName, packageName, className);
+        }
+    }
+
+    @Override
+    public ParceledListSlice<Property> queryProperty(
+            String propertyName, @PropertyLocation int componentType) {
+        Objects.requireNonNull(propertyName);
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getCallingUserId();
+        final List<Property> result =
+                mPackageProperty.queryProperty(propertyName, componentType, packageName -> {
+                    final PackageSetting ps = getPackageSetting(packageName);
+                    return shouldFilterApplicationLocked(ps, callingUid, callingUserId);
+                });
+        if (result == null) {
+            return ParceledListSlice.emptyList();
+        }
+        return new ParceledListSlice<>(result);
+    }
+
     /**
      * Adds a scanned package to the system. When this method is finished, the package will
      * be available for query, resolution, etc...
@@ -12703,27 +12858,7 @@
             final boolean isReplace =
                     reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace;
             mAppsFilter.addPackage(pkgSetting, isReplace);
-
-            // Don't allow ephemeral applications to define new permissions groups.
-            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                Slog.w(TAG, "Permission groups from package " + pkg.getPackageName()
-                        + " ignored: instant apps cannot define new permission groups.");
-            } else {
-                mPermissionManager.addAllPermissionGroups(pkg, chatty);
-            }
-
-            // If a permission has had its defining app changed, or it has had its protection
-            // upgraded, we need to revoke apps that hold it
-            final List<String> permissionsWithChangedDefinition;
-            // Don't allow ephemeral applications to define new permissions.
-            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                permissionsWithChangedDefinition = null;
-                Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
-                        + " ignored: instant apps cannot define new permissions.");
-            } else {
-                permissionsWithChangedDefinition =
-                        mPermissionManager.addAllPermissions(pkg, chatty);
-            }
+            mPackageProperty.addAllProperties(pkg);
 
             int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
             StringBuilder r = null;
@@ -12751,31 +12886,7 @@
                 }
             }
 
-            boolean hasOldPkg = oldPkg != null;
-            boolean hasPermissionDefinitionChanges =
-                    !CollectionUtils.isEmpty(permissionsWithChangedDefinition);
-            if (hasOldPkg || hasPermissionDefinitionChanges) {
-                // We need to call revokeRuntimePermissionsIfGroupChanged async as permission
-                // revoke callbacks from this method might need to kill apps which need the
-                // mPackages lock on a different thread. This would dead lock.
-                //
-                // Hence create a copy of all package names and pass it into
-                // revokeRuntimePermissionsIfGroupChanged. Only for those permissions might get
-                // revoked. If a new package is added before the async code runs the permission
-                // won't be granted yet, hence new packages are no problem.
-                final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
-
-                AsyncTask.execute(() -> {
-                    if (hasOldPkg) {
-                        mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
-                                allPackageNames);
-                    }
-                    if (hasPermissionDefinitionChanges) {
-                        mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
-                                permissionsWithChangedDefinition, allPackageNames);
-                    }
-                });
-            }
+            mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
         }
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -12874,7 +12985,7 @@
         }
     }
 
-    void removePackageLI(String packageName, boolean chatty) {
+    private void removePackageLI(String packageName, boolean chatty) {
         if (DEBUG_INSTALL) {
             if (chatty)
                 Log.d(TAG, "Removing package " + packageName);
@@ -12889,9 +13000,10 @@
         }
     }
 
-    void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
+    private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
         mComponentResolver.removeAllComponents(pkg, chatty);
-        mPermissionManager.removeAllPermissions(pkg, chatty);
+        mPermissionManager.onPackageRemoved(pkg);
+        mPackageProperty.removeAllProperties(pkg);
 
         final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
         StringBuilder r = null;
@@ -13271,9 +13383,8 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "setApplicationHiddenSetting for user " + userId);
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
 
         if (hidden && isPackageDeviceAdmin(packageName, userId)) {
             Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
@@ -13459,9 +13570,8 @@
     public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getApplicationHidden for user " + userId);
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "getApplicationHidden for user " + userId);
         PackageSetting ps;
         final long callingId = Binder.clearCallingIdentity();
         try {
@@ -13511,9 +13621,8 @@
                     + android.Manifest.permission.INSTALL_PACKAGES + ".");
         }
         PackageSetting pkgSetting;
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "installExistingPackage for user " + userId);
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                true /* checkShell */, "installExistingPackage for user " + userId);
         if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
@@ -13878,9 +13987,8 @@
     @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "isPackageSuspendedForUser for user " + userId);
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -17718,6 +17826,17 @@
         }
     }
 
+    /*
+     * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+     * as this only works for packages that are installed
+     *
+     * TODO: Move logic for permission group compatibility into PermissionManagerService
+     */
+    @SuppressWarnings("AndroidFrameworkCompatChange")
+    private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
+        return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+    }
+
     @GuardedBy("mInstallLock")
     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
             throws PrepareFailure {
@@ -17760,7 +17879,7 @@
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
-        ParsedPackage parsedPackage;
+        final ParsedPackage parsedPackage;
         try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
                 mPackageParserCallback)) {
             parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
@@ -17876,15 +17995,6 @@
                 }
             }
 
-            /*
-             * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
-             * as this only works for packages that are installed
-             *
-             * TODO: Move logic for permission group compatibility into PermissionManagerService
-             */
-            boolean cannotInstallWithBadPermissionGroups =
-                    parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
-
             PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (ps != null) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -17942,7 +18052,8 @@
                         parsedPackage.getPermissionGroups().get(groupNum);
                 final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
 
-                if (sourceGroup != null && cannotInstallWithBadPermissionGroups) {
+                if (sourceGroup != null
+                        && cannotInstallWithBadPermissionGroups(parsedPackage)) {
                     final String sourcePackageName = sourceGroup.packageName;
 
                     if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
@@ -18017,7 +18128,8 @@
                     }
                 }
 
-                if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) {
+                if (perm.getGroup() != null
+                        && cannotInstallWithBadPermissionGroups(parsedPackage)) {
                     boolean isPermGroupDefinedByPackage = false;
                     for (int groupNum = 0; groupNum < numGroups; groupNum++) {
                         if (parsedPackage.getPermissionGroups().get(groupNum).getName()
@@ -19389,33 +19501,13 @@
                     if (outInfo != null) {
                         outInfo.removedAppId = removedAppId;
                     }
-                    if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0)
-                            && !isUpdatedSystemApp(deletedPs)) {
-                        mPermissionManager.removeAppIdStateTEMP(removedAppId);
+                    final SharedUserSetting sus = deletedPs.getSharedUser();
+                    List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null;
+                    if (sharedUserPkgs == null) {
+                        sharedUserPkgs = Collections.emptyList();
                     }
-                    mPermissionManager.updatePermissions(deletedPs.name, null);
-                    if (deletedPs.sharedUser != null) {
-                        // Remove permissions associated with package. Since runtime
-                        // permissions are per user we have to kill the removed package
-                        // or packages running under the shared user of the removed
-                        // package if revoking the permissions requested only by the removed
-                        // package is successful and this causes a change in gids.
-                        boolean shouldKill = false;
-                        for (int userId : UserManagerService.getInstance().getUserIds()) {
-                            final int userIdToKill = mPermissionManager
-                                    .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs,
-                                            userId);
-                            shouldKill |= userIdToKill != UserHandle.USER_NULL;
-                        }
-                        // If gids changed, kill all affected packages.
-                        if (shouldKill) {
-                            mHandler.post(() -> {
-                                // This has to happen with no lock held.
-                                killApplication(deletedPs.name, deletedPs.appId,
-                                        KILL_APP_REASON_GIDS_CHANGED);
-                            });
-                        }
-                    }
+                    mPermissionManager.onPackageStateRemoved(packageName, deletedPs.appId,
+                            deletedPs.pkg, sharedUserPkgs);
                     clearPackagePreferredActivitiesLPw(
                             deletedPs.name, changedUsers, UserHandle.USER_ALL);
                 }
@@ -19987,8 +20079,8 @@
                 android.Manifest.permission.CLEAR_APP_USER_DATA, null);
 
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */, "clear application data");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "clear application data");
 
         final boolean filterApp;
         synchronized (mLock) {
@@ -20141,9 +20233,8 @@
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
         }
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                /* requireFullPermission= */ true, /* checkShell= */ false,
-                "delete application cache files");
+        enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true,
+                /* checkShell= */ false, "delete application cache files");
         final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_INSTANT_APPS);
 
@@ -20269,8 +20360,8 @@
             String opname, boolean removeExisting) {
         // writer
         int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "add preferred activity");
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -20344,9 +20435,8 @@
         }
 
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "replace preferred activity");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "replace preferred activity");
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -21555,8 +21645,8 @@
             permission = mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
         }
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /* requireFullPermission */, true /* checkShell */, "set enabled");
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                true /* checkShell */, "set enabled");
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
         boolean sendNow = false;
         boolean isApp = (className == null);
@@ -21781,7 +21871,7 @@
         if (!mUserManager.exists(userId)) {
             return;
         }
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
                 false /* checkShell */, "flushPackageRestrictions");
         synchronized (mLock) {
             flushPackageRestrictionsAsUserInternalLocked(userId);
@@ -21846,8 +21936,8 @@
         final int permission = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */, "stop package");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                true /* checkShell */, "stop package");
         // writer
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -21994,8 +22084,8 @@
     public int getApplicationEnabledSetting(String packageName, int userId) {
         if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
         int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /* requireFullPermission */, false /* checkShell */, "get enabled");
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get enabled");
         // reader
         synchronized (mLock) {
             try {
@@ -22015,8 +22105,8 @@
         if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
         if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
         int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "getComponentEnabled");
         synchronized (mLock) {
             try {
                 if (shouldFilterApplicationLocked(
@@ -26111,9 +26201,8 @@
     @Override
     public int getInstallReason(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "get install reason");
+        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "get install reason");
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -26195,9 +26284,8 @@
     public String getInstantAppAndroidId(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
                 "getInstantAppAndroidId");
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getInstantAppAndroidId");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+                false /* checkShell */, "getInstantAppAndroidId");
         // Make sure the target is an Instant App.
         if (!isInstantApp(packageName, userId)) {
             return null;
@@ -26313,8 +26401,8 @@
         final int callingUid = Binder.getCallingUid();
         final int callingAppId = UserHandle.getAppId(callingUid);
 
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo");
+        enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
+                true /*checkShell*/, "setHarmfulAppInfo");
 
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
                 checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
@@ -26334,8 +26422,8 @@
         final int callingUid = Binder.getCallingUid();
         final int callingAppId = UserHandle.getAppId(callingUid);
 
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo");
+        enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
+                true /*checkShell*/, "getHarmfulAppInfo");
 
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
                 checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
@@ -26353,8 +26441,8 @@
         final int callingUid = Binder.getCallingUid();
         final int callingAppId = UserHandle.getAppId(callingUid);
 
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                false /*requireFullPermission*/, true /*checkShell*/, "isPackageStateProtected");
+        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                true /*checkShell*/, "isPackageStateProtected");
 
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
                 && checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b05fd47..9f1d656 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -46,7 +46,6 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.V4Signature;
 import android.os.incremental.V4Signature.HashingInfo;
diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java
new file mode 100644
index 0000000..d18a02d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageProperty.java
@@ -0,0 +1,287 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.TYPE_ACTIVITY;
+import static android.content.pm.PackageManager.TYPE_APPLICATION;
+import static android.content.pm.PackageManager.TYPE_PROVIDER;
+import static android.content.pm.PackageManager.TYPE_RECEIVER;
+import static android.content.pm.PackageManager.TYPE_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.PackageManager.PropertyLocation;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Manages properties defined within a package using the &lt;property&gt; tag.
+ */
+public class PackageProperty {
+    /**
+     * Mapping of property name to all defined defined properties.
+     * <p>This is a mapping of property name --> package map. The package
+     * map is a mapping of package name -> list of properties.
+     */
+    private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mApplicationProperties;
+    private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mActivityProperties;
+    private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mProviderProperties;
+    private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mReceiverProperties;
+    private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mServiceProperties;
+
+    /**
+     * If the provided component is {@code null}, returns the property defined on the
+     * application. Otherwise, returns the property defined on the component.
+     */
+    public Property getProperty(@NonNull String propertyName, @NonNull String packageName,
+            @Nullable String className) {
+        if (className == null) {
+            return getApplicationProperty(propertyName, packageName);
+        }
+        return getComponentProperty(propertyName, packageName, className);
+    }
+
+    /**
+     * Returns all properties defined at the given location.
+     * <p>Valid locations are {@link PackageManager#TYPE_APPLICATION},
+     * {@link PackageManager#TYPE_ACTIVITY}, {@link PackageManager#TYPE_PROVIDER},
+     * {@link PackageManager#TYPE_RECEIVER}, or {@link PackageManager#TYPE_SERVICE}.
+     */
+    public List<Property> queryProperty(@NonNull String propertyName,
+            @PropertyLocation int componentType, Predicate<String> filter) {
+        final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap;
+        if (componentType == TYPE_APPLICATION) {
+            propertyMap = mApplicationProperties;
+        } else if (componentType == TYPE_ACTIVITY) {
+            propertyMap = mActivityProperties;
+        } else if (componentType == TYPE_PROVIDER) {
+            propertyMap = mProviderProperties;
+        } else if (componentType == TYPE_RECEIVER) {
+            propertyMap = mReceiverProperties;
+        } else if (componentType == TYPE_SERVICE) {
+            propertyMap = mServiceProperties;
+        } else {
+            propertyMap = null;
+        }
+        if (propertyMap == null) {
+            return null;
+        }
+        final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
+                propertyMap.get(propertyName);
+        if (packagePropertyMap == null) {
+            return null;
+        }
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getCallingUserId();
+        final int mapSize = packagePropertyMap.size();
+        final List<Property> result = new ArrayList<>(mapSize);
+        for (int i = 0; i < mapSize; i++) {
+            final String packageName = packagePropertyMap.keyAt(i);
+            if (filter.test(packageName)) {
+                continue;
+            }
+            result.addAll(packagePropertyMap.valueAt(i));
+        }
+        return result;
+    }
+
+    /** Adds all properties defined for the given package */
+    void addAllProperties(AndroidPackage pkg) {
+        mApplicationProperties = addProperties(pkg.getProperties(), mApplicationProperties);
+        mActivityProperties = addComponentProperties(pkg.getActivities(), mActivityProperties);
+        mProviderProperties = addComponentProperties(pkg.getProviders(), mProviderProperties);
+        mReceiverProperties = addComponentProperties(pkg.getReceivers(), mReceiverProperties);
+        mServiceProperties = addComponentProperties(pkg.getServices(), mServiceProperties);
+    }
+
+    /** Adds all properties defined for the given package */
+    void removeAllProperties(AndroidPackage pkg) {
+        mApplicationProperties = removeProperties(pkg.getProperties(), mApplicationProperties);
+        mActivityProperties = removeComponentProperties(pkg.getActivities(), mActivityProperties);
+        mProviderProperties = removeComponentProperties(pkg.getProviders(), mProviderProperties);
+        mReceiverProperties = removeComponentProperties(pkg.getReceivers(), mReceiverProperties);
+        mServiceProperties = removeComponentProperties(pkg.getServices(), mServiceProperties);
+    }
+
+    /** Add the properties defined on the given components to the property collection */
+    private static <T extends ParsedComponent>
+                ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addComponentProperties(
+            @NonNull List<T> components,
+            @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
+        ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
+                propertyCollection;
+        final int componentsSize = components.size();
+        for (int i = 0; i < componentsSize; i++) {
+            final Map<String, Property> properties = components.get(i).getProperties();
+            if (properties.size() == 0) {
+                continue;
+            }
+            returnCollection = addProperties(properties, returnCollection);
+        }
+        return returnCollection;
+    }
+
+    /** Add the given properties to the property collection */
+    private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addProperties(
+            @NonNull Map<String, Property> properties,
+            @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
+        if (properties.size() == 0) {
+            return propertyCollection;
+        }
+        final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
+                propertyCollection == null ? new ArrayMap<>(10) : propertyCollection;
+        final Iterator<Property> iter = properties.values().iterator();
+        while (iter.hasNext()) {
+            final Property property = iter.next();
+            final String propertyName = property.getName();
+            final String packageName = property.getPackageName();
+            ArrayMap<String, ArrayList<Property>> propertyMap = returnCollection.get(propertyName);
+            if (propertyMap == null) {
+                propertyMap = new ArrayMap<>();
+                returnCollection.put(propertyName, propertyMap);
+            }
+            ArrayList<Property> packageProperties = propertyMap.get(packageName);
+            if (packageProperties == null) {
+                packageProperties = new ArrayList<>(properties.size());
+                propertyMap.put(packageName, packageProperties);
+            }
+            packageProperties.add(property);
+        }
+        return returnCollection;
+    }
+
+    /** Removes the properties defined on the given components from the property collection */
+    private static <T extends ParsedComponent>
+                ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeComponentProperties(
+            @NonNull List<T> components,
+            @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
+        ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection =
+                propertyCollection;
+        final int componentsSize = components.size();
+        for (int i = 0; returnCollection != null && i < componentsSize; i++) {
+            final Map<String, Property> properties = components.get(i).getProperties();
+            if (properties.size() == 0) {
+                continue;
+            }
+            returnCollection = removeProperties(properties, returnCollection);
+        }
+        return returnCollection;
+    }
+
+    /** Removes the given properties from the property collection */
+    private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeProperties(
+            @NonNull Map<String, Property> properties,
+            @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) {
+        if (propertyCollection == null) {
+            return null;
+        }
+        final Iterator<Property> iter = properties.values().iterator();
+        while (iter.hasNext()) {
+            final Property property = iter.next();
+            final String propertyName = property.getName();
+            final String packageName = property.getPackageName();
+            ArrayMap<String, ArrayList<Property>> propertyMap =
+                    propertyCollection.get(propertyName);
+            if (propertyMap == null) {
+                // error
+                continue;
+            }
+            ArrayList<Property> packageProperties = propertyMap.get(packageName);
+            if (packageProperties == null) {
+                //error
+                continue;
+            }
+            packageProperties.remove(property);
+
+            // clean up empty structures
+            if (packageProperties.size() == 0) {
+                propertyMap.remove(packageName);
+            }
+            if (propertyMap.size() == 0) {
+                propertyCollection.remove(propertyName);
+            }
+        }
+        if (propertyCollection.size() == 0) {
+            return null;
+        }
+        return propertyCollection;
+    }
+
+    private static Property getProperty(String propertyName, String packageName, String className,
+            ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap) {
+        final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
+                propertyMap.get(propertyName);
+        if (packagePropertyMap == null) {
+            return null;
+        }
+        final List<Property> propertyList = packagePropertyMap.get(packageName);
+        if (propertyList == null) {
+            return null;
+        }
+        for (int i = propertyList.size() - 1; i >= 0; i--) {
+            final Property property = propertyList.get(i);
+            if (Objects.equals(className, property.getClassName())) {
+                return property;
+            }
+        }
+        return null;
+    }
+
+    private Property getComponentProperty(
+            String propertyName, String packageName, String className) {
+        Property property = null;
+        if (property == null && mActivityProperties != null) {
+            property = getProperty(propertyName, packageName, className, mActivityProperties);
+        }
+        if (property == null && mProviderProperties != null) {
+            property = getProperty(propertyName, packageName, className, mProviderProperties);
+        }
+        if (property == null && mReceiverProperties != null) {
+            property = getProperty(propertyName, packageName, className, mReceiverProperties);
+        }
+        if (property == null && mServiceProperties != null) {
+            property = getProperty(propertyName, packageName, className, mServiceProperties);
+        }
+        return property;
+    }
+
+    private Property getApplicationProperty(String propertyName, String packageName) {
+        final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
+                mApplicationProperties.get(propertyName);
+        if (packagePropertyMap == null) {
+            return null;
+        }
+        final List<Property> propertyList = packagePropertyMap.get(packageName);
+        if (propertyList == null) {
+            return null;
+        }
+        return propertyList.get(0);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 69721d0..804faa1 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -252,43 +252,6 @@
         return numMatch == NS;
     }
 
-    public boolean sameSet(PreferredComponent pc) {
-        if (mSetPackages == null || pc == null || pc.mSetPackages == null
-                || !sameComponent(pc.mComponent)) {
-            return false;
-        }
-        final int otherPackageCount = pc.mSetPackages.length;
-        final int packageCount = mSetPackages.length;
-        int numMatch = 0;
-        for (int i = 0; i < otherPackageCount; i++) {
-            boolean good = false;
-            for (int j = 0; j < packageCount; j++) {
-                if (mSetPackages[j].equals(pc.mSetPackages[j])
-                        && mSetClasses[j].equals(pc.mSetClasses[j])) {
-                    numMatch++;
-                    good = true;
-                    break;
-                }
-            }
-            if (!good) {
-                return false;
-            }
-        }
-        return numMatch == packageCount;
-    }
-
-    /** Returns true if the preferred component represents the provided ComponentName. */
-    private boolean sameComponent(ComponentName comp) {
-        if (mComponent == null || comp == null) {
-            return false;
-        }
-        if (mComponent.getPackageName().equals(comp.getPackageName())
-                && mComponent.getClassName().equals(comp.getClassName())) {
-            return true;
-        }
-        return false;
-    }
-
     public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
         if (mSetPackages == null) {
             return query == null;
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index ff3df13..a261e29 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -22,7 +22,6 @@
 import java.io.PrintWriter;
 
 import com.android.server.IntentResolver;
-import java.util.ArrayList;
 
 public class PreferredIntentResolver
         extends IntentResolver<PreferredActivity, PreferredActivity> {
@@ -46,24 +45,4 @@
     protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) {
         return input;
     }
-
-    public boolean shouldAddPreferredActivity(PreferredActivity pa) {
-        ArrayList<PreferredActivity> pal = findFilters(pa);
-        if (pal == null || pal.isEmpty()) {
-            return true;
-        }
-        if (!pa.mPref.mAlways) {
-            return false;
-        }
-        final int activityCount = pal.size();
-        for (int i = 0; i < activityCount; i++) {
-            PreferredActivity cur = pal.get(i);
-            if (cur.mPref.mAlways
-                    && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK)
-                    && cur.mPref.sameSet(pa.mPref)) {
-                return false;
-            }
-        }
-        return true;
-    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9a5e05f..966090cb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1312,7 +1312,8 @@
                 PreferredActivity pa = new PreferredActivity(parser);
                 if (pa.mPref.getParseError() == null) {
                     final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
-                    if (resolver.shouldAddPreferredActivity(pa)) {
+                    ArrayList<PreferredActivity> pal = resolver.findFilters(pa);
+                    if (pal == null || pal.size() == 0 || pa.mPref.mAlways) {
                         resolver.addFilter(pa);
                     }
                 } else {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d7c46a5..2d77182 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -83,7 +83,6 @@
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.text.TextUtils;
 import android.text.format.TimeMigrationUtils;
 import android.util.ArraySet;
@@ -2838,10 +2837,14 @@
                 int queryFlags, int userId, int callingPid, int callingUid) {
             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
 
-            final boolean cloneKeyFieldOnly =
-                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
-            final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
-                    : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+            int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+            if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) {
+                flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+            } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+                flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON;
+            }
+            final int cloneFlag = flags;
+
             if (packageName == null) {
                 shortcutIds = null; // LauncherAppsService already threw for it though.
             }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index d535f7b..85c4ab2 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -49,7 +49,6 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
similarity index 98%
rename from services/core/java/android/os/UserManagerInternal.java
rename to services/core/java/com/android/server/pm/UserManagerInternal.java
index 76b5ba5..47d0a3d5 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.os;
+package com.android.server.pm;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -22,8 +22,8 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
-
-import com.android.server.pm.RestrictionsSet;
+import android.os.Bundle;
+import android.os.UserManager;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 14352f3..c51e75c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -78,8 +78,6 @@
 import android.os.UserManager;
 import android.os.UserManager.EnforcingUser;
 import android.os.UserManager.QuietModeFlag;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
@@ -112,6 +110,7 @@
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
 import com.android.server.am.UserState;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 145dd24..d0c3a95 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -33,7 +33,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.telephony.SubscriptionInfo;
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 0245b28..687e96c 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -399,8 +399,7 @@
     @NonNull
     public static Permission createOrUpdate(@Nullable Permission permission,
             @NonNull PermissionInfo permissionInfo, @NonNull AndroidPackage pkg,
-            @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission,
-            boolean chatty) {
+            @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission) {
         // Allow system apps to redefine non-system permissions
         boolean ownerChanged = false;
         if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName,
@@ -437,7 +436,7 @@
                     permission.mPermissionInfo = permissionInfo;
                     permission.mReconciled = true;
                     permission.mUid = pkg.getUid();
-                    if (chatty) {
+                    if (PackageManagerService.DEBUG_PACKAGE_SCANNING) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -456,7 +455,7 @@
                         + permissionInfo.packageName + " ignored: original from "
                         + permission.mPermissionInfo.packageName);
             }
-        } else if (chatty) {
+        } else if (PackageManagerService.DEBUG_PACKAGE_SCANNING) {
             if (r == null) {
                 r = new StringBuilder(256);
             } else {
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 f819063..1b35d29 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -77,7 +77,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.PermissionChecker;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -94,8 +93,10 @@
 import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -107,7 +108,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.permission.IOnPermissionsChangeListener;
 import android.permission.IPermissionManager;
@@ -134,6 +134,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IntPair;
 import com.android.internal.util.Preconditions;
@@ -146,7 +147,7 @@
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.PackageManagerServiceUtils;
 import com.android.server.pm.PackageSetting;
-import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -754,7 +755,6 @@
         enforceCrossUserPermission(callingUid, userId,
                 true,  // requireFullPermission
                 false, // checkShell
-                false, // requirePermissionWhenSameUser
                 "getPermissionFlags");
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -841,7 +841,6 @@
         enforceCrossUserPermission(callingUid, userId,
                 true,  // requireFullPermission
                 true,  // checkShell
-                false, // requirePermissionWhenSameUser
                 "updatePermissionFlags");
 
         if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) {
@@ -951,7 +950,6 @@
         enforceCrossUserPermission(callingUid, userId,
                 true,  // requireFullPermission
                 true,  // checkShell
-                false, // requirePermissionWhenSameUser
                 "updatePermissionFlagsForAllApps");
 
         // Only the system can change system fixed flags.
@@ -1555,7 +1553,6 @@
         enforceCrossUserPermission(callingUid, userId,
                 true,  // requireFullPermission
                 true,  // checkShell
-                false, // requirePermissionWhenSameUser
                 "grantRuntimePermission");
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -1722,7 +1719,6 @@
         enforceCrossUserPermission(callingUid, userId,
                 true,  // requireFullPermission
                 true,  // checkShell
-                false, // requirePermissionWhenSameUser
                 "revokeRuntimePermission");
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -2368,14 +2364,9 @@
      *
      * @param newPackage The new package that was installed
      * @param oldPackage The old package that was updated
-     * @param allPackageNames All package names
-     * @param permissionCallback Callback for permission changed
      */
-    private void revokeRuntimePermissionsIfGroupChanged(
-            @NonNull AndroidPackage newPackage,
-            @NonNull AndroidPackage oldPackage,
-            @NonNull ArrayList<String> allPackageNames,
-            @NonNull PermissionCallback permissionCallback) {
+    private void revokeRuntimePermissionsIfGroupChangedInternal(@NonNull AndroidPackage newPackage,
+            @NonNull AndroidPackage oldPackage) {
         final int numOldPackagePermissions = ArrayUtils.size(oldPackage.getPermissions());
         final ArrayMap<String, String> oldPermissionNameToGroupName
                 = new ArrayMap<>(numOldPackagePermissions);
@@ -2408,13 +2399,9 @@
                 if (newPermissionGroupName != null
                         && !newPermissionGroupName.equals(oldPermissionGroupName)) {
                     final int[] userIds = mUserManagerInt.getUserIds();
-                    final int numUserIds = userIds.length;
-                    for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
-                        final int userId = userIds[userIdNum];
-
-                        final int numPackages = allPackageNames.size();
-                        for (int packageNum = 0; packageNum < numPackages; packageNum++) {
-                            final String packageName = allPackageNames.get(packageNum);
+                    mPackageManagerInt.forEachPackage(pkg -> {
+                        final String packageName = pkg.getPackageName();
+                        for (final int userId : userIds) {
                             final int permissionState = checkPermission(permissionName, packageName,
                                     userId);
                             if (permissionState == PackageManager.PERMISSION_GRANTED) {
@@ -2427,14 +2414,15 @@
 
                                 try {
                                     revokeRuntimePermissionInternal(permissionName, packageName,
-                                            false, callingUid, userId, null, permissionCallback);
+                                            false, callingUid, userId, null,
+                                            mDefaultPermissionCallback);
                                 } catch (IllegalArgumentException e) {
                                     Slog.e(TAG, "Could not revoke " + permissionName + " from "
                                             + packageName, e);
                                 }
                             }
                         }
-                    }
+                    });
                 }
             }
         }
@@ -2445,18 +2433,11 @@
      * granted permissions must be revoked.
      *
      * @param permissionsToRevoke A list of permission names to revoke
-     * @param allPackageNames All package names
-     * @param permissionCallback Callback for permission changed
      */
-    private void revokeRuntimePermissionsIfPermissionDefinitionChanged(
-            @NonNull List<String> permissionsToRevoke,
-            @NonNull ArrayList<String> allPackageNames,
-            @NonNull PermissionCallback permissionCallback) {
-
+    private void revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
+            @NonNull List<String> permissionsToRevoke) {
         final int[] userIds = mUserManagerInt.getUserIds();
         final int numPermissions = permissionsToRevoke.size();
-        final int numUserIds = userIds.length;
-        final int numPackages = allPackageNames.size();
         final int callingUid = Binder.getCallingUid();
 
         for (int permNum = 0; permNum < numPermissions; permNum++) {
@@ -2467,15 +2448,14 @@
                     continue;
                 }
             }
-            for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
-                final int userId = userIds[userIdNum];
-                for (int packageNum = 0; packageNum < numPackages; packageNum++) {
-                    final String packageName = allPackageNames.get(packageNum);
-                    final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
-                    if (uid < Process.FIRST_APPLICATION_UID) {
-                        // do not revoke from system apps
-                        continue;
-                    }
+            mPackageManagerInt.forEachPackage(pkg -> {
+                final String packageName = pkg.getPackageName();
+                final int appId = pkg.getUid();
+                if (appId < Process.FIRST_APPLICATION_UID) {
+                    // do not revoke from system apps
+                    return;
+                }
+                for (final int userId : userIds) {
                     final int permissionState = checkPermissionImpl(permName, packageName,
                             userId);
                     final int flags = getPermissionFlags(permName, packageName, userId);
@@ -2485,6 +2465,7 @@
                             | FLAG_PERMISSION_GRANTED_BY_ROLE;
                     if (permissionState == PackageManager.PERMISSION_GRANTED
                             && (flags & flagMask) == 0) {
+                        final int uid = UserHandle.getUid(userId, appId);
                         EventLog.writeEvent(0x534e4554, "154505240", uid,
                                 "Revoking permission " + permName + " from package "
                                         + packageName + " due to definition change");
@@ -2495,18 +2476,18 @@
                                 + packageName + " due to definition change");
                         try {
                             revokeRuntimePermissionInternal(permName, packageName,
-                                    false, callingUid, userId, null, permissionCallback);
+                                    false, callingUid, userId, null, mDefaultPermissionCallback);
                         } catch (Exception e) {
                             Slog.e(TAG, "Could not revoke " + permName + " from "
                                     + packageName, e);
                         }
                     }
                 }
-            }
+            });
         }
     }
 
-    private List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) {
+    private List<String> addAllPermissionsInternal(@NonNull AndroidPackage pkg) {
         final int N = ArrayUtils.size(pkg.getPermissions());
         ArrayList<String> definitionChangedPermissions = new ArrayList<>();
         for (int i=0; i<N; i++) {
@@ -2544,7 +2525,7 @@
             synchronized (mLock) {
                 final Permission permission = Permission.createOrUpdate(oldPermission,
                         permissionInfo, pkg, mRegistry.getPermissionTrees(),
-                        isOverridingSystemPermission, chatty);
+                        isOverridingSystemPermission);
                 if (p.isTree()) {
                     mRegistry.addPermissionTree(permission);
                 } else {
@@ -2562,7 +2543,7 @@
         return definitionChangedPermissions;
     }
 
-    private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
+    private void addAllPermissionGroupsInternal(@NonNull AndroidPackage pkg) {
         synchronized (mLock) {
             final int N = ArrayUtils.size(pkg.getPermissionGroups());
             StringBuilder r = null;
@@ -2573,7 +2554,7 @@
                 final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName);
                 if (cur == null || isPackageUpdate) {
                     mRegistry.addPermissionGroup(pg);
-                    if (chatty && DEBUG_PACKAGE_SCANNING) {
+                    if (DEBUG_PACKAGE_SCANNING) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -2588,7 +2569,7 @@
                     Slog.w(TAG, "Permission group " + pg.getName() + " from package "
                             + pg.getPackageName() + " ignored: original from "
                             + cur.getPackageName());
-                    if (chatty && DEBUG_PACKAGE_SCANNING) {
+                    if (DEBUG_PACKAGE_SCANNING) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -2605,7 +2586,7 @@
         }
     }
 
-    private void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
+    private void removeAllPermissionsInternal(@NonNull AndroidPackage pkg) {
         synchronized (mLock) {
             int N = ArrayUtils.size(pkg.getPermissions());
             StringBuilder r = null;
@@ -2617,7 +2598,7 @@
                 }
                 if (bp != null && bp.isPermission(p)) {
                     bp.setPermissionInfo(null);
-                    if (DEBUG_REMOVE && chatty) {
+                    if (DEBUG_REMOVE) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -3995,34 +3976,30 @@
     }
 
     @UserIdInt
-    private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs,
+    private int revokeSharedUserPermissionsForDeletedPackageInternal(
+            @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
             @UserIdInt int userId) {
-        if ((deletedPs == null) || (deletedPs.pkg == null)) {
+        if (pkg == null) {
             Slog.i(TAG, "Trying to update info for null package. Just ignoring");
             return UserHandle.USER_NULL;
         }
 
-        SharedUserSetting sus = deletedPs.getSharedUser();
-
-        // No sharedUserId
-        if (sus == null) {
+        // No shared user packages
+        if (sharedUserPkgs.isEmpty()) {
             return UserHandle.USER_NULL;
         }
 
         int affectedUserId = UserHandle.USER_NULL;
         // Update permissions
-        for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) {
+        for (String eachPerm : pkg.getRequestedPermissions()) {
             // Check if another package in the shared user needs the permission.
             boolean used = false;
-            final List<AndroidPackage> pkgs = sus.getPackages();
-            if (pkgs != null) {
-                for (AndroidPackage pkg : pkgs) {
-                    if (pkg != null
-                            && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName())
-                            && pkg.getRequestedPermissions().contains(eachPerm)) {
-                        used = true;
-                        break;
-                    }
+            for (AndroidPackage sharedUserpkg : sharedUserPkgs) {
+                if (sharedUserpkg != null
+                        && !sharedUserpkg.getPackageName().equals(pkg.getPackageName())
+                        && sharedUserpkg.getRequestedPermissions().contains(eachPerm)) {
+                    used = true;
+                    break;
                 }
             }
             if (used) {
@@ -4030,7 +4007,7 @@
             }
 
             PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
-                    deletedPs.pkg.getPackageName());
+                    pkg.getPackageName());
 
             // If the package is shadowing is a disabled system package,
             // do not drop permissions that the shadowed package requests.
@@ -4048,9 +4025,9 @@
             }
 
             synchronized (mLock) {
-                UidPermissionState uidState = getUidStateLocked(deletedPs.pkg, userId);
+                UidPermissionState uidState = getUidStateLocked(pkg, userId);
                 if (uidState == null) {
-                    Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+                    Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
                             + " and user " + userId);
                     continue;
                 }
@@ -4494,25 +4471,22 @@
     }
 
     /**
-     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
-     * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+     *
      * @param checkShell whether to prevent shell from access if there's a debugging restriction
      * @param message the message to log on security exception
      */
     private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
-            boolean requireFullPermission, boolean checkShell,
-            boolean requirePermissionWhenSameUser, String message) {
+            boolean requireFullPermission, boolean checkShell, @Nullable String message) {
         if (userId < 0) {
             throw new IllegalArgumentException("Invalid userId " + userId);
         }
         if (checkShell) {
-            PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
-                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+            enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
         }
         final int callingUserId = UserHandle.getUserId(callingUid);
-        if (hasCrossUserPermission(
-                callingUid, callingUserId, userId, requireFullPermission,
-                requirePermissionWhenSameUser)) {
+        if (checkCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission)) {
             return;
         }
         String errorMessage = buildInvalidCrossUserPermissionMessage(
@@ -4522,82 +4496,45 @@
     }
 
     /**
-     * Checks if the request is from the system or an app that has the appropriate cross-user
-     * permissions defined as follows:
-     * <ul>
-     * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
-     * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group
-     * to the caller.</li>
-     * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group
-     * as the caller.</li>
-     * </ul>
-     *
-     * @param checkShell whether to prevent shell from access if there's a debugging restriction
-     * @param message the message to log on security exception
+     *  Enforces that if the caller is shell, it does not have the provided user restriction.
      */
-    private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
-            boolean requireFullPermission, boolean checkShell,
-            String message) {
-        if (userId < 0) {
-            throw new IllegalArgumentException("Invalid userId " + userId);
+    private void enforceShellRestriction(@NonNull String restriction, int callingUid,
+            @UserIdInt int userId) {
+        if (callingUid == Process.SHELL_UID) {
+            if (userId >= 0 && mUserManagerInt.hasUserRestriction(restriction, userId)) {
+                throw new SecurityException("Shell does not have permission to access user "
+                        + userId);
+            } else if (userId < 0) {
+                Slog.e(LOG_TAG, "Unable to check shell permission for user "
+                        + userId + "\n\t" + Debug.getCallers(3));
+            }
         }
-        if (checkShell) {
-            PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
-                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
-        }
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
-                /*requirePermissionWhenSameUser= */ false)) {
-            return;
-        }
-        final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
-        if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
-                mContext,
-                android.Manifest.permission.INTERACT_ACROSS_PROFILES,
-                PermissionChecker.PID_UNKNOWN,
-                callingUid,
-                mPackageManagerInt.getPackage(callingUid).getPackageName())
-                == PermissionChecker.PERMISSION_GRANTED) {
-            return;
-        }
-        String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
-                callingUid, userId, message, requireFullPermission, isSameProfileGroup);
-        Slog.w(TAG, errorMessage);
-        throw new SecurityException(errorMessage);
     }
 
-    private boolean hasCrossUserPermission(
-            int callingUid, int callingUserId, int userId, boolean requireFullPermission,
-            boolean requirePermissionWhenSameUser) {
-        if (!requirePermissionWhenSameUser && userId == callingUserId) {
+    private boolean checkCrossUserPermission(int callingUid, @UserIdInt int callingUserId,
+            @UserIdInt int userId, boolean requireFullPermission) {
+        if (userId == callingUserId) {
             return true;
         }
         if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
             return true;
         }
         if (requireFullPermission) {
-            return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+            return checkCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
         }
-        return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+        return checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                || checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
     }
 
-    private boolean hasPermission(String permission) {
+    private boolean checkCallingOrSelfPermission(String permission) {
         return mContext.checkCallingOrSelfPermission(permission)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
+    @NonNull
     private static String buildInvalidCrossUserPermissionMessage(int callingUid,
-            @UserIdInt int userId, String message, boolean requireFullPermission) {
+            @UserIdInt int userId, @Nullable String message, boolean requireFullPermission) {
         StringBuilder builder = new StringBuilder();
         if (message != null) {
             builder.append(message);
@@ -4617,31 +4554,6 @@
         return builder.toString();
     }
 
-    private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
-            @UserIdInt int userId, String message, boolean requireFullPermission,
-            boolean isSameProfileGroup) {
-        StringBuilder builder = new StringBuilder();
-        if (message != null) {
-            builder.append(message);
-            builder.append(": ");
-        }
-        builder.append("UID ");
-        builder.append(callingUid);
-        builder.append(" requires ");
-        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-        if (!requireFullPermission) {
-            builder.append(" or ");
-            builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
-            if (isSameProfileGroup) {
-                builder.append(" or ");
-                builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
-            }
-        }
-        builder.append(" to access user ");
-        builder.append(".");
-        return builder.toString();
-    }
-
     @GuardedBy("mLock")
     private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) {
         int size = 0;
@@ -4918,10 +4830,113 @@
         }
     }
 
-    private void transferPermissions(@NonNull String oldPackageName,
-            @NonNull String newPackageName) {
-        synchronized (mLock) {
-            mRegistry.transferPermissions(oldPackageName, newPackageName);
+    private void onPackageAddedInternal(@NonNull AndroidPackage pkg, boolean isInstantApp,
+            @Nullable AndroidPackage oldPkg) {
+        if (!pkg.getAdoptPermissions().isEmpty()) {
+            // This package wants to adopt ownership of permissions from
+            // another package.
+            for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) {
+                final String origName = pkg.getAdoptPermissions().get(i);
+                if (canAdoptPermissionsInternal(origName, pkg)) {
+                    Slog.i(TAG, "Adopting permissions from " + origName + " to "
+                            + pkg.getPackageName());
+                    synchronized (mLock) {
+                        mRegistry.transferPermissions(origName, pkg.getPackageName());
+                    }
+                }
+            }
+        }
+
+        // Don't allow ephemeral applications to define new permissions groups.
+        if (isInstantApp) {
+            Slog.w(TAG, "Permission groups from package " + pkg.getPackageName()
+                    + " ignored: instant apps cannot define new permission groups.");
+        } else {
+            addAllPermissionGroupsInternal(pkg);
+        }
+
+        // If a permission has had its defining app changed, or it has had its protection
+        // upgraded, we need to revoke apps that hold it
+        final List<String> permissionsWithChangedDefinition;
+        // Don't allow ephemeral applications to define new permissions.
+        if (isInstantApp) {
+            permissionsWithChangedDefinition = null;
+            Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
+                    + " ignored: instant apps cannot define new permissions.");
+        } else {
+            permissionsWithChangedDefinition = addAllPermissionsInternal(pkg);
+        }
+
+        boolean hasOldPkg = oldPkg != null;
+        boolean hasPermissionDefinitionChanges =
+                !CollectionUtils.isEmpty(permissionsWithChangedDefinition);
+        if (hasOldPkg || hasPermissionDefinitionChanges) {
+            // We need to call revokeRuntimePermissionsIfGroupChanged async as permission
+            // revoke callbacks from this method might need to kill apps which need the
+            // mPackages lock on a different thread. This would dead lock.
+            AsyncTask.execute(() -> {
+                if (hasOldPkg) {
+                    revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
+                }
+                if (hasPermissionDefinitionChanges) {
+                    revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
+                            permissionsWithChangedDefinition);
+                }
+            });
+        }
+    }
+
+    private boolean canAdoptPermissionsInternal(@NonNull String oldPackageName,
+            @NonNull AndroidPackage newPkg) {
+        final PackageSetting oldPs = mPackageManagerInt.getPackageSetting(oldPackageName);
+        if (oldPs == null) {
+            return false;
+        }
+        if (!oldPs.isSystem()) {
+            Slog.w(TAG, "Unable to update from " + oldPs.name
+                    + " to " + newPkg.getPackageName()
+                    + ": old package not in system partition");
+            return false;
+        }
+        if (mPackageManagerInt.getPackage(oldPs.name) != null) {
+            Slog.w(TAG, "Unable to update from " + oldPs.name
+                    + " to " + newPkg.getPackageName()
+                    + ": old package still exists");
+            return false;
+        }
+        return true;
+    }
+
+    private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) {
+        removeAllPermissionsInternal(pkg);
+    }
+
+    private void onPackageStateRemovedInternal(@NonNull String packageName, int appId,
+            @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) {
+        if (sharedUserPkgs.isEmpty()
+                && mPackageManagerInt.getDisabledSystemPackage(packageName) == null) {
+            removeAppIdState(appId);
+        }
+        updatePermissions(packageName, null, mDefaultPermissionCallback);
+        if (!sharedUserPkgs.isEmpty()) {
+            // Remove permissions associated with package. Since runtime
+            // permissions are per user we have to kill the removed package
+            // or packages running under the shared user of the removed
+            // package if revoking the permissions requested only by the removed
+            // package is successful and this causes a change in gids.
+            boolean shouldKill = false;
+            for (int userId : UserManagerService.getInstance().getUserIds()) {
+                final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
+                        sharedUserPkgs, userId);
+                shouldKill |= userIdToKill != UserHandle.USER_NULL;
+            }
+            // If gids changed, kill all affected packages.
+            if (shouldKill) {
+                mHandler.post(() -> {
+                    // This has to happen with no lock held.
+                    killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED);
+                });
+            }
         }
     }
 
@@ -5025,35 +5040,6 @@
         }
 
         @Override
-        public void revokeRuntimePermissionsIfGroupChanged(
-                @NonNull AndroidPackage newPackage,
-                @NonNull AndroidPackage oldPackage,
-                @NonNull ArrayList<String> allPackageNames) {
-            PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
-                    oldPackage, allPackageNames, mDefaultPermissionCallback);
-        }
-
-        @Override
-        public void revokeRuntimePermissionsIfPermissionDefinitionChanged(
-                @NonNull List<String> permissionsToRevoke,
-                @NonNull ArrayList<String> allPackageNames) {
-            PermissionManagerService.this.revokeRuntimePermissionsIfPermissionDefinitionChanged(
-                    permissionsToRevoke, allPackageNames, mDefaultPermissionCallback);
-        }
-
-        @Override
-        public List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) {
-            return PermissionManagerService.this.addAllPermissions(pkg, chatty);
-        }
-        @Override
-        public void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
-            PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
-        }
-        @Override
-        public void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
-            PermissionManagerService.this.removeAllPermissions(pkg, chatty);
-        }
-        @Override
         public void readLegacyPermissionStateTEMP() {
             PermissionManagerService.this.readLegacyPermissionState();
         }
@@ -5065,17 +5051,6 @@
         public void onUserRemoved(@UserIdInt int userId) {
             PermissionManagerService.this.onUserRemoved(userId);
         }
-        @Override
-        public void removeAppIdStateTEMP(@AppIdInt int appId) {
-            PermissionManagerService.this.removeAppIdState(appId);
-        }
-        @Override
-        @UserIdInt
-        public int revokeSharedUserPermissionsForDeletedPackageTEMP(
-                @NonNull PackageSetting deletedPs, @UserIdInt int userId) {
-            return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage(
-                    deletedPs, userId);
-        }
         @NonNull
         @Override
         public Set<String> getGrantedPermissions(@NonNull String packageName,
@@ -5140,30 +5115,6 @@
             Preconditions.checkArgumentNonNegative(userId, "userId");
             mPackageManagerInt.forEachPackage(pkg -> resetRuntimePermissionsInternal(pkg, userId));
         }
-        @Override
-        public void enforceCrossUserPermission(int callingUid, int userId,
-                boolean requireFullPermission, boolean checkShell, String message) {
-            PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
-                    requireFullPermission, checkShell, false, message);
-        }
-        @Override
-        public void enforceCrossUserPermission(int callingUid, int userId,
-                boolean requireFullPermission, boolean checkShell,
-                boolean requirePermissionWhenSameUser, String message) {
-            PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
-                    requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
-        }
-
-        @Override
-        public void enforceCrossUserOrProfilePermission(int callingUid, int userId,
-                boolean requireFullPermission, boolean checkShell, String message) {
-            PermissionManagerService.this.enforceCrossUserOrProfilePermission(
-                    callingUid,
-                    userId,
-                    requireFullPermission,
-                    checkShell,
-                    message);
-        }
 
         @Override
         public Permission getPermissionTEMP(String permName) {
@@ -5407,9 +5358,24 @@
         }
 
         @Override
-        public void transferPermissions(@NonNull String oldPackageName,
-                @NonNull String newPackageName) {
-            PermissionManagerService.this.transferPermissions(oldPackageName, newPackageName);
+        public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
+                @Nullable AndroidPackage oldPkg) {
+            Objects.requireNonNull(pkg);
+            onPackageAddedInternal(pkg, isInstantApp, oldPkg);
+        }
+
+        @Override
+        public void onPackageRemoved(@NonNull AndroidPackage pkg) {
+            Objects.requireNonNull(pkg);
+            onPackageRemovedInternal(pkg);
+        }
+
+        @Override
+        public void onPackageStateRemoved(@NonNull String packageName, int appId,
+                @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) {
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(sharedUserPkgs);
+            onPackageStateRemovedInternal(packageName, appId, pkg, sharedUserPkgs);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 7656b2e..1becbed 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -24,7 +24,6 @@
 import android.content.pm.PermissionInfo;
 import android.permission.PermissionManagerInternal;
 
-import com.android.server.pm.PackageSetting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.util.ArrayList;
@@ -277,44 +276,6 @@
     public abstract void resetAllRuntimePermissions(@UserIdInt int userId);
 
     /**
-     * We might auto-grant permissions if any permission of the group is already granted. Hence if
-     * the group of a granted permission changes we need to revoke it to avoid having permissions of
-     * the new group auto-granted.
-     *
-     * @param newPackage The new package that was installed
-     * @param oldPackage The old package that was updated
-     * @param allPackageNames All packages
-     */
-    public abstract void revokeRuntimePermissionsIfGroupChanged(
-            @NonNull AndroidPackage newPackage,
-            @NonNull AndroidPackage oldPackage,
-            @NonNull ArrayList<String> allPackageNames);
-
-    /**
-     * Some permissions might have been owned by a non-system package, and the system then defined
-     * said permission. Some other permissions may one have been install permissions, but are now
-     * runtime or higher. These permissions should be revoked.
-     *
-     * @param permissionsToRevoke A list of permission names to revoke
-     * @param allPackageNames All packages
-     */
-    public abstract void revokeRuntimePermissionsIfPermissionDefinitionChanged(
-            @NonNull List<String> permissionsToRevoke,
-            @NonNull ArrayList<String> allPackageNames);
-
-    /**
-     * Add all permissions in the given package.
-     * <p>
-     * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
-     * the permission settings.
-     *
-     * @return A list of BasePermissions that were updated, and need to be revoked from packages
-     */
-    public abstract List<String> addAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
-    public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty);
-    public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
-
-    /**
      * Read legacy permission state from package settings.
      *
      * TODO(zhanghai): This is a temporary method because we should not expose
@@ -337,30 +298,6 @@
     public abstract void onUserRemoved(@UserIdInt int userId);
 
     /**
-     * Remove the permission state associated with an app ID, called the same time as the
-     * removal of a {@code PackageSetitng}.
-     *
-     * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app
-     * ID removal via API.
-     */
-    public abstract void removeAppIdStateTEMP(@AppIdInt int appId);
-
-    /**
-     * Update the shared user setting when a package with a shared user id is removed. The gids
-     * associated with each permission of the deleted package are removed from the shared user'
-     * gid list only if its not in use by other permissions of packages in the shared user setting.
-     *
-     * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package
-     * state where the updated system package is uninstalled but the disabled system package is yet
-     * to be installed. Then we should handle this in restorePermissionState().
-     *
-     * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none.
-     */
-    @UserIdInt
-    public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP(
-            @NonNull PackageSetting deletedPs, @UserIdInt int userId);
-
-    /**
      * Get all the permissions granted to a package.
      *
      * @param packageName the name of the package
@@ -393,32 +330,6 @@
     @NonNull
     public abstract String[] getAppOpPermissionPackages(@NonNull String permissionName);
 
-    /**
-     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
-     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller.
-     * @param checkShell whether to prevent shell from access if there's a debugging restriction
-     * @param message the message to log on security exception
-     */
-    public abstract void enforceCrossUserPermission(int callingUid, int userId,
-            boolean requireFullPermission, boolean checkShell, @NonNull String message);
-
-    /**
-     * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)}
-     * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are
-     * in the same profile group.
-     */
-    public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId,
-            boolean requireFullPermission, boolean checkShell, @NonNull String message);
-
-    /**
-     * @see #enforceCrossUserPermission(int, int, boolean, boolean, String)
-     * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
-     * permission to be held even if the callingUid and userId reference the same user.
-     */
-    public abstract void enforceCrossUserPermission(int callingUid, int userId,
-            boolean requireFullPermission, boolean checkShell,
-            boolean requirePermissionWhenSameUser, @NonNull String message);
-
     /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
     @Nullable
     public abstract Permission getPermissionTEMP(@NonNull String permName);
@@ -604,10 +515,35 @@
             @NonNull LegacyPermissionSettings legacyPermissionSettings);
 
     /**
-     * Transfers ownership of permissions from one package to another.
+     * Callback when a package has been added.
+     *
+     * @param pkg the added package
+     * @param isInstantApp whether the added package is an instant app
+     * @param oldPkg the old package, or {@code null} if none
      */
-    public abstract void transferPermissions(@NonNull String oldPackageName,
-            @NonNull String newPackageName);
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    public abstract void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
+            @Nullable AndroidPackage oldPkg);
+
+    /**
+     * Callback when a package has been removed.
+     *
+     * @param pkg the removed package
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    public abstract void onPackageRemoved(@NonNull AndroidPackage pkg);
+
+    /**
+     * Callback when the state for a package has been removed.
+     *
+     * @param packageName the name of the removed package
+     * @param appId the app ID of the removed package
+     * @param pkg the removed package, or {@code null} if unavailable
+     * @param sharedUserPkgs the packages that are in the same shared user
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    public abstract void onPackageStateRemoved(@NonNull String packageName, int appId,
+            @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs);
 
     /**
      * Check whether a permission can be propagated to instant app.
diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
index 54f6183..154f9a4 100644
--- a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
@@ -17,6 +17,7 @@
 package com.android.server.policy;
 
 import android.annotation.NonNull;
+import android.content.Context;
 
 import com.android.server.devicestate.DeviceStatePolicy;
 import com.android.server.devicestate.DeviceStateProvider;
@@ -27,10 +28,12 @@
  * @see DeviceStateProviderImpl
  */
 public final class DeviceStatePolicyImpl implements DeviceStatePolicy {
+    private final Context mContext;
     private final DeviceStateProvider mProvider;
 
-    public DeviceStatePolicyImpl() {
-        mProvider = new DeviceStateProviderImpl();
+    public DeviceStatePolicyImpl(Context context) {
+        mContext = context;
+        mProvider = DeviceStateProviderImpl.create(mContext);
     }
 
     public DeviceStateProvider getDeviceStateProvider() {
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 85ab0bc..321bb8c 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -16,30 +16,464 @@
 
 package com.android.server.policy;
 
-import android.annotation.Nullable;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.input.InputManagerInternal;
+import android.os.Environment;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.policy.devicestate.config.Conditions;
+import com.android.server.policy.devicestate.config.DeviceState;
+import com.android.server.policy.devicestate.config.DeviceStateConfig;
+import com.android.server.policy.devicestate.config.LidSwitchCondition;
+import com.android.server.policy.devicestate.config.NumericRange;
+import com.android.server.policy.devicestate.config.SensorCondition;
+import com.android.server.policy.devicestate.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BooleanSupplier;
+
+import javax.xml.datatype.DatatypeConfigurationException;
 
 /**
- * Default implementation of {@link DeviceStateProvider}. Currently only supports
- * {@link #DEFAULT_DEVICE_STATE}.
- *
- * @see DeviceStatePolicyImpl
+ * Implementation of {@link DeviceStateProvider} that reads the set of supported device states
+ * from a configuration file provided at either /vendor/etc/devicestate or
+ * /data/system/devicestate/. By default, the provider supports {@link #DEFAULT_DEVICE_STATE} when
+ * no configuration is provided.
  */
-final class DeviceStateProviderImpl implements DeviceStateProvider {
-    private static final int DEFAULT_DEVICE_STATE = 0;
+public final class DeviceStateProviderImpl implements DeviceStateProvider,
+        InputManagerInternal.LidSwitchCallback, SensorEventListener {
+    private static final String TAG = "DeviceStateProviderImpl";
+
+    private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
+
+    @VisibleForTesting
+    static final int DEFAULT_DEVICE_STATE = 0;
+
+    private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
+    private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
+    private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
+
+    /** Interface that allows reading the device state configuration. */
+    interface ReadableConfig {
+        @NonNull
+        InputStream openRead() throws IOException;
+    }
+
+    /**
+     * Returns a new {@link DeviceStateProviderImpl} instance.
+     *
+     * @param context the {@link Context} that should be used to access system services.
+     */
+    public static DeviceStateProviderImpl create(@NonNull Context context) {
+        File configFile = getConfigurationFile();
+        if (configFile == null) {
+            return createFromConfig(context, null);
+        }
+        return createFromConfig(context, new ReadableFileConfig(configFile));
+    }
+
+    /**
+     * Returns a new {@link DeviceStateProviderImpl} instance.
+     *
+     * @param context the {@link Context} that should be used to access system services.
+     * @param readableConfig the config the provider instance should read supported states from.
+     */
+    @VisibleForTesting
+    static DeviceStateProviderImpl createFromConfig(@NonNull Context context,
+            @Nullable ReadableConfig readableConfig) {
+        SparseArray<Conditions> conditionsForState = new SparseArray<>();
+        if (readableConfig != null) {
+            DeviceStateConfig config = parseConfig(readableConfig);
+            if (config != null) {
+                for (DeviceState stateConfig : config.getDeviceState()) {
+                    int state = stateConfig.getIdentifier().intValue();
+                    Conditions conditions = stateConfig.getConditions();
+                    conditionsForState.put(state, conditions);
+                }
+            }
+        }
+
+        if (conditionsForState.size() == 0) {
+            conditionsForState.put(DEFAULT_DEVICE_STATE, null);
+        }
+        return new DeviceStateProviderImpl(context, conditionsForState);
+    }
+
+    // Lock for internal state.
+    private final Object mLock = new Object();
+    private final Context mContext;
+    // List of supported states in ascending order.
+    private final int[] mOrderedStates;
+    // Map of state to a boolean supplier that returns true when all required conditions are met for
+    // the device to be in the state.
+    private final SparseArray<BooleanSupplier> mStateConditions;
 
     @Nullable
+    @GuardedBy("mLock")
     private Listener mListener = null;
+    @GuardedBy("mLock")
+    private int mLastReportedState = INVALID_DEVICE_STATE;
+
+    @GuardedBy("mLock")
+    private boolean mIsLidOpen;
+    @GuardedBy("mLock")
+    private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
+
+    private DeviceStateProviderImpl(@NonNull Context context,
+            @NonNull SparseArray<Conditions> conditionsForState) {
+        mContext = context;
+        mOrderedStates = new int[conditionsForState.size()];
+        for (int i = 0; i < conditionsForState.size(); i++) {
+            mOrderedStates[i] = conditionsForState.keyAt(i);
+        }
+
+        // Whether or not this instance should register to receive lid switch notifications from
+        // InputManagerInternal. If there are no device state conditions that are based on the lid
+        // switch there is no need to register for a callback.
+        boolean shouldListenToLidSwitch = false;
+
+        final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
+        // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from.
+        final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
+
+        mStateConditions = new SparseArray<>();
+        for (int i = 0; i < mOrderedStates.length; i++) {
+            int state = mOrderedStates[i];
+            Conditions conditions = conditionsForState.get(state);
+            if (conditions == null) {
+                mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+                continue;
+            }
+
+            List<BooleanSupplier> suppliers = new ArrayList<>();
+
+            LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
+            if (lidSwitchCondition != null) {
+                suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
+                shouldListenToLidSwitch = true;
+            }
+
+            List<SensorCondition> sensorConditions = conditions.getSensor();
+            for (int j = 0; j < sensorConditions.size(); j++) {
+                SensorCondition sensorCondition = sensorConditions.get(j);
+                final int expectedSensorType = sensorCondition.getType().intValue();
+                final String expectedSensorName = sensorCondition.getName();
+
+                List<Sensor> sensors = sensorManager.getSensorList(expectedSensorType);
+                Sensor foundSensor = null;
+                for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) {
+                    Sensor sensor = sensors.get(sensorIndex);
+                    if (sensor.getName().equals(expectedSensorName)) {
+                        foundSensor = sensor;
+                        break;
+                    }
+                }
+
+                if (foundSensor == null) {
+                    throw new IllegalStateException("Failed to find Sensor with type: "
+                            + expectedSensorType + " and name: " + expectedSensorName);
+                }
+
+                suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
+                sensorsToListenTo.add(foundSensor);
+            }
+
+            if (suppliers.size() > 1) {
+                mStateConditions.put(state, new AndBooleanSupplier(suppliers));
+            } else if (suppliers.size() > 0) {
+                // No need to wrap with an AND supplier if there is only 1.
+                mStateConditions.put(state, suppliers.get(0));
+            } else {
+                // There are no conditions for this state. Default to always true.
+                mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+            }
+        }
+
+        if (shouldListenToLidSwitch) {
+            InputManagerInternal inputManager = LocalServices.getService(
+                    InputManagerInternal.class);
+            inputManager.registerLidSwitchCallback(this);
+        }
+
+        for (int i = 0; i < sensorsToListenTo.size(); i++) {
+            Sensor sensor = sensorsToListenTo.valueAt(i);
+            sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
+        }
+    }
 
     @Override
     public void setListener(Listener listener) {
-        if (mListener != null) {
-            throw new RuntimeException("Provider already has a listener set.");
+        synchronized (mLock) {
+            if (mListener != null) {
+                throw new RuntimeException("Provider already has a listener set.");
+            }
+            mListener = listener;
+        }
+        notifySupportedStatesChanged();
+        notifyDeviceStateChangedIfNeeded();
+    }
+
+    /** Notifies the listener that the set of supported device states has changed. */
+    private void notifySupportedStatesChanged() {
+        int[] supportedStates;
+        synchronized (mLock) {
+            if (mListener == null) {
+                return;
+            }
+
+            supportedStates = Arrays.copyOf(mOrderedStates, mOrderedStates.length);
         }
 
-        mListener = listener;
-        mListener.onSupportedDeviceStatesChanged(new int[]{ DEFAULT_DEVICE_STATE });
-        mListener.onStateChanged(DEFAULT_DEVICE_STATE);
+        mListener.onSupportedDeviceStatesChanged(supportedStates);
+    }
+
+    /** Computes the current device state and notifies the listener of a change, if needed. */
+    void notifyDeviceStateChangedIfNeeded() {
+        int stateToReport = INVALID_DEVICE_STATE;
+        synchronized (mLock) {
+            if (mListener == null) {
+                return;
+            }
+
+            int newState = mOrderedStates[0];
+            for (int i = 1; i < mOrderedStates.length; i++) {
+                int state = mOrderedStates[i];
+                if (mStateConditions.get(state).getAsBoolean()) {
+                    newState = state;
+                    break;
+                }
+            }
+
+            if (newState != mLastReportedState) {
+                mLastReportedState = newState;
+                stateToReport = newState;
+            }
+        }
+
+        if (stateToReport != INVALID_DEVICE_STATE) {
+            mListener.onStateChanged(stateToReport);
+        }
+    }
+
+    @Override
+    public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+        synchronized (mLock) {
+            mIsLidOpen = lidOpen;
+        }
+        notifyDeviceStateChangedIfNeeded();
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        synchronized (mLock) {
+            mLatestSensorEvent.put(event.sensor, event);
+        }
+        notifyDeviceStateChangedIfNeeded();
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // Do nothing.
+    }
+
+    /**
+     * Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid
+     * switch open state matches {@link #mIsLidOpen}.
+     */
+    private final class LidSwitchBooleanSupplier implements BooleanSupplier {
+        private final boolean mExpectedOpen;
+
+        LidSwitchBooleanSupplier(boolean expectedOpen) {
+            mExpectedOpen = expectedOpen;
+        }
+
+        @Override
+        public boolean getAsBoolean() {
+            synchronized (mLock) {
+                return mIsLidOpen == mExpectedOpen;
+            }
+        }
+    }
+
+    /**
+     * Implementation of {@link BooleanSupplier} that returns {@code true} if the latest
+     * {@link SensorEvent#values sensor event values} for the specified {@link Sensor} adhere to
+     * the supplied {@link NumericRange ranges}.
+     */
+    private final class SensorBooleanSupplier implements BooleanSupplier {
+        @NonNull
+        private final Sensor mSensor;
+        @NonNull
+        private final List<NumericRange> mExpectedValues;
+
+        SensorBooleanSupplier(@NonNull Sensor sensor, @NonNull List<NumericRange> expectedValues) {
+            mSensor = sensor;
+            mExpectedValues = expectedValues;
+        }
+
+        @Override
+        public boolean getAsBoolean() {
+            synchronized (mLock) {
+                SensorEvent latestEvent = mLatestSensorEvent.get(mSensor);
+                if (latestEvent == null) {
+                    // Default to returning false if we have not yet received a sensor event for the
+                    // sensor.
+                    return false;
+                }
+
+                if (latestEvent.values.length != mExpectedValues.size()) {
+                    throw new IllegalStateException("Number of supplied numeric range(s) does not "
+                            + "match the number of values in the latest sensor event for sensor: "
+                            + mSensor);
+                }
+
+                for (int i = 0; i < latestEvent.values.length; i++) {
+                    if (!adheresToRange(latestEvent.values[i], mExpectedValues.get(i))) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+
+        /**
+         * Returns {@code true} if the supplied {@code value} adheres to the constraints specified
+         * in {@code range}.
+         */
+        private boolean adheresToRange(float value, @NonNull NumericRange range) {
+            final BigDecimal min = range.getMin_optional();
+            if (min != null) {
+                if (value <= min.floatValue()) {
+                    return false;
+                }
+            }
+
+            final BigDecimal minInclusive = range.getMinInclusive_optional();
+            if (minInclusive != null) {
+                if (value < minInclusive.floatValue()) {
+                    return false;
+                }
+            }
+
+            final BigDecimal max = range.getMax_optional();
+            if (max != null) {
+                if (value >= max.floatValue()) {
+                    return false;
+                }
+            }
+
+            final BigDecimal maxInclusive = range.getMaxInclusive_optional();
+            if (maxInclusive != null) {
+                if (value > maxInclusive.floatValue()) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+
+    /**
+     * Implementation of {@link BooleanSupplier} whose result is the product of an AND operation
+     * applied to the result of all child suppliers.
+     */
+    private static final class AndBooleanSupplier implements BooleanSupplier {
+        @NonNull
+        List<BooleanSupplier> mBooleanSuppliers;
+
+        AndBooleanSupplier(@NonNull List<BooleanSupplier> booleanSuppliers) {
+            mBooleanSuppliers = booleanSuppliers;
+        }
+
+        @Override
+        public boolean getAsBoolean() {
+            for (int i = 0; i < mBooleanSuppliers.size(); i++) {
+                if (!mBooleanSuppliers.get(i).getAsBoolean()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Returns the device state configuration file that should be used, or {@code null} if no file
+     * is present on the device.
+     * <p>
+     * Defaults to returning a config file present in the data/ dir at
+     * {@link #DATA_CONFIG_FILE_PATH}, and then falls back to the config file in the vendor/ dir
+     * at {@link #VENDOR_CONFIG_FILE_PATH} if no config file is found in the data/ dir.
+     */
+    @Nullable
+    private static File getConfigurationFile() {
+        final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(),
+                DATA_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
+        if (configFileFromDataDir.exists()) {
+            return configFileFromDataDir;
+        }
+
+        final File configFileFromVendorDir = Environment.buildPath(Environment.getVendorDirectory(),
+                VENDOR_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
+        if (configFileFromVendorDir.exists()) {
+            return configFileFromVendorDir;
+        }
+
+        return null;
+    }
+
+    /**
+     * Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns
+     * {@code null} if the file could not be successfully parsed.
+     */
+    @Nullable
+    private static DeviceStateConfig parseConfig(@NonNull ReadableConfig readableConfig) {
+        try (InputStream in = readableConfig.openRead();
+                InputStream bin = new BufferedInputStream(in)) {
+            return XmlParser.read(bin);
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading device state config", e);
+        }
+        return null;
+    }
+
+    /** Implementation of {@link ReadableConfig} that reads config data from a file. */
+    private static final class ReadableFileConfig implements ReadableConfig {
+        @NonNull
+        private final File mFile;
+
+        private ReadableFileConfig(@NonNull File file) {
+            mFile = file;
+        }
+
+        @Override
+        public InputStream openRead() throws IOException {
+            return new FileInputStream(mFile);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index e1cd9e3..9077433 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -47,7 +47,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.permission.PermissionControllerManager;
 import android.provider.Settings;
 import android.provider.Telephony;
@@ -68,7 +67,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f7e6822..8beec35eb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.AppTransition;
 import com.android.server.wm.DisplayPolicy;
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowManagerInternal;
@@ -1959,14 +1958,14 @@
 
         mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
             @Override
-            public int onAppTransitionStartingLocked(int transit, long duration,
+            public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
                     long statusBarAnimationStartTime, long statusBarAnimationDuration) {
-                return handleStartTransitionForKeyguardLw(transit, duration);
+                return handleStartTransitionForKeyguardLw(keyguardGoingAway, duration);
             }
 
             @Override
-            public void onAppTransitionCancelledLocked(int transit) {
-                handleStartTransitionForKeyguardLw(transit, 0 /* duration */);
+            public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+                handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
             }
         });
         mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
@@ -3194,7 +3193,7 @@
         }
     }
 
-    private int handleStartTransitionForKeyguardLw(int transit, long duration) {
+    private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
         if (mKeyguardOccludedChanged) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
                     + mPendingKeyguardOccluded);
@@ -3203,7 +3202,7 @@
                 return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
             }
         }
-        if (AppTransition.isKeyguardGoingAwayTransit(transit)) {
+        if (keyguardGoingAway) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
             startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
         }
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
new file mode 100644
index 0000000..6477552
--- /dev/null
+++ b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
@@ -0,0 +1,122 @@
+/*
+ * 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.power;
+
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.DisplayGroup;
+
+/**
+ * Responsible for creating {@link DisplayPowerRequest}s and associating them with
+ * {@link com.android.server.display.DisplayGroup}s.
+ *
+ * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest}
+ * which is used to request power state changes to every display in the group.
+ */
+class DisplayPowerRequestMapper {
+
+    private final Object mLock = new Object();
+
+    /** A mapping from LogicalDisplay Id to DisplayGroup Id. */
+    @GuardedBy("mLock")
+    private final SparseIntArray mDisplayGroupIds = new SparseIntArray();
+
+    /** A mapping from DisplayGroup Id to DisplayPowerRequest. */
+    @GuardedBy("mLock")
+    private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>();
+
+    private final DisplayManagerInternal mDisplayManagerInternal;
+
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+
+                @Override
+                public void onDisplayAdded(int displayId) {
+                    synchronized (mLock) {
+                        if (mDisplayGroupIds.indexOfKey(displayId) >= 0) {
+                            return;
+                        }
+                        final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId(
+                                displayId);
+                        if (!mDisplayPowerRequests.contains(displayGroupId)) {
+                            // A new DisplayGroup was created; create a new DisplayPowerRequest.
+                            mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest());
+                        }
+                        mDisplayGroupIds.append(displayId, displayGroupId);
+                    }
+                }
+
+                @Override
+                public void onDisplayRemoved(int displayId) {
+                    synchronized (mLock) {
+                        final int index = mDisplayGroupIds.indexOfKey(displayId);
+                        if (index < 0) {
+                            return;
+                        }
+                        final int displayGroupId = mDisplayGroupIds.valueAt(index);
+                        mDisplayGroupIds.removeAt(index);
+
+                        if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) {
+                            // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
+                            mDisplayPowerRequests.delete(displayGroupId);
+                        }
+                    }
+                }
+
+                @Override
+                public void onDisplayChanged(int displayId) {
+                    synchronized (mLock) {
+                        final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId(
+                                displayId);
+                        final int oldDisplayGroupId = mDisplayGroupIds.get(displayId);
+
+                        if (!mDisplayPowerRequests.contains(newDisplayGroupId)) {
+                            // A new DisplayGroup was created; create a new DisplayPowerRequest.
+                            mDisplayPowerRequests.append(newDisplayGroupId,
+                                    new DisplayPowerRequest());
+                        }
+                        mDisplayGroupIds.put(displayId, newDisplayGroupId);
+
+                        if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) {
+                            // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
+                            mDisplayPowerRequests.delete(oldDisplayGroupId);
+                        }
+                    }
+                }
+            };
+
+    DisplayPowerRequestMapper(DisplayManager displayManager,
+            DisplayManagerInternal displayManagerInternal, Handler handler) {
+        mDisplayManagerInternal = displayManagerInternal;
+        displayManager.registerDisplayListener(mDisplayListener, handler);
+        mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest());
+        mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT);
+    }
+
+    DisplayPowerRequest get(int displayId) {
+        synchronized (mLock) {
+            return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ccd659d..955e1cd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -42,6 +42,7 @@
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.hardware.power.Boost;
@@ -348,9 +349,9 @@
     // A bitfield that summarizes the effect of the user activity timer.
     private int mUserActivitySummary;
 
-    // The desired display power state.  The actual state may lag behind the
+    // Manages the desired power state of displays. The actual state may lag behind the
     // requested because it is updated asynchronously by the display power controller.
-    private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
+    private DisplayPowerRequestMapper mDisplayPowerRequestMapper;
 
     // True if the display power state has been fully applied, which means the display
     // is actually on or actually off or whatever was requested.
@@ -1054,6 +1055,8 @@
             mPolicy = getLocalService(WindowManagerPolicy.class);
             mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
             mAttentionDetector.systemReady(mContext);
+            mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService(
+                    DisplayManager.class), mDisplayManagerInternal, mHandler);
 
             SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
@@ -2296,10 +2299,12 @@
                         && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
                     nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
                     if (now < nextTimeout) {
-                        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
-                                || mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
+                        final DisplayPowerRequest displayPowerRequest =
+                                mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+                        if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
+                                || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
                             mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
-                        } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+                        } else if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
                             mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                         }
                     }
@@ -2771,11 +2776,13 @@
      * Returns true if the device is allowed to dream in its current state.
      */
     private boolean canDreamLocked() {
+        final DisplayPowerRequest displayPowerRequest =
+                mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
         if (getWakefulnessLocked() != WAKEFULNESS_DREAMING
                 || !mDreamsSupportedConfig
                 || !mDreamsEnabledSetting
-                || !mDisplayPowerRequest.isBrightOrDim()
-                || mDisplayPowerRequest.isVr()
+                || !displayPowerRequest.isBrightOrDim()
+                || displayPowerRequest.isVr()
                 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                         | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
                 || !mBootCompleted) {
@@ -2826,7 +2833,9 @@
                 sQuiescent = false;
             }
 
-            mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
+            final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
+                    Display.DEFAULT_DISPLAY);
+            displayPowerRequest.policy = getDesiredScreenPolicyLocked();
 
             // Determine appropriate screen brightness and auto-brightness adjustments.
             final boolean autoBrightness;
@@ -2846,39 +2855,39 @@
             }
 
             // Update display power request.
-            mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
-            mDisplayPowerRequest.useAutoBrightness = autoBrightness;
-            mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
-            mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
+            displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
+            displayPowerRequest.useAutoBrightness = autoBrightness;
+            displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+            displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
 
-            updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest);
+            updatePowerRequestFromBatterySaverPolicy(displayPowerRequest);
 
-            if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
-                mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+            if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
+                displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
                 if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
                         && !mDrawWakeLockOverrideFromSidekick) {
-                    if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
-                        mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+                    if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
+                        displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
                     }
-                    if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
-                        mDisplayPowerRequest.dozeScreenState = Display.STATE_ON;
+                    if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
+                        displayPowerRequest.dozeScreenState = Display.STATE_ON;
                     }
                 }
-                mDisplayPowerRequest.dozeScreenBrightness =
+                displayPowerRequest.dozeScreenBrightness =
                         mDozeScreenBrightnessOverrideFromDreamManagerFloat;
             } else {
-                mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
-                mDisplayPowerRequest.dozeScreenBrightness =
+                displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
+                displayPowerRequest.dozeScreenBrightness =
                         PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
 
-            mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
+            mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest,
                     mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
-                        + ", policy=" + mDisplayPowerRequest.policy
+                        + ", policy=" + displayPowerRequest.policy
                         + ", mWakefulness=" + getWakefulnessLocked()
                         + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                         + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
@@ -3049,7 +3058,9 @@
         final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
         final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
         final boolean autoSuspend = !needDisplaySuspendBlocker;
-        final boolean interactive = mDisplayPowerRequest.isBrightOrDim();
+        final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
+                Display.DEFAULT_DISPLAY);
+        final boolean interactive = displayPowerRequest.isBrightOrDim();
 
         // Disable auto-suspend if needed.
         // FIXME We should consider just leaving auto-suspend enabled forever since
@@ -3108,19 +3119,21 @@
         if (!mDisplayReady) {
             return true;
         }
-        if (mDisplayPowerRequest.isBrightOrDim()) {
+        final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
+                Display.DEFAULT_DISPLAY);
+        if (displayPowerRequest.isBrightOrDim()) {
             // If we asked for the screen to be on but it is off due to the proximity
             // sensor then we may suspend but only if the configuration allows it.
             // On some hardware it may not be safe to suspend because the proximity
             // sensor may not be correctly configured as a wake-up source.
-            if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive
+            if (!displayPowerRequest.useProximitySensor || !mProximityPositive
                     || !mSuspendWhenScreenOffDueToProximityConfig) {
                 return true;
             }
         }
 
-        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
-                && mDisplayPowerRequest.dozeScreenState == Display.STATE_ON) {
+        if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
+                && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
             // Although we are in DOZE and would normally allow the device to suspend,
             // the doze service has explicitly requested the display to remain in the ON
             // state which means we should hold the display suspend blocker.
@@ -5575,7 +5588,10 @@
             // DisplayPowerController only reports proximity positive (near) if it's
             // positive and the proximity wasn't already being ignored. So it reliably
             // also tells us that we're not already ignoring the proximity sensor.
-            if (mDisplayPowerRequest.useProximitySensor && mProximityPositive) {
+
+            final DisplayPowerRequest displayPowerRequest =
+                    mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+            if (displayPowerRequest.useProximitySensor && mProximityPositive) {
                 mDisplayManagerInternal.ignoreProximitySensorUntilChanged();
                 return true;
             }
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index e7b1b4e..ab6ada2 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -46,7 +46,6 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -71,7 +70,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import java.io.ByteArrayOutputStream;
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 1c29c69..b79fc8d 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -35,7 +35,6 @@
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.StorageVolume;
 import android.service.storage.ExternalStorageService;
@@ -46,6 +45,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.IOException;
 import java.util.HashMap;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index afbe552..510893b 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -87,6 +87,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.SystemService;
@@ -337,6 +338,7 @@
                 inputState = new TvInputState();
             }
             inputState.info = info;
+            inputState.uid = getInputUid(info);
             inputMap.put(info.getId(), inputState);
         }
 
@@ -371,6 +373,16 @@
         userState.inputMap = inputMap;
     }
 
+    private int getInputUid(TvInputInfo info) {
+        try {
+            return getContext().getPackageManager().getApplicationInfo(
+                    info.getServiceInfo().packageName, 0).uid;
+        } catch (NameNotFoundException e) {
+            Slog.w(TAG, "Unable to get UID for  " + info, e);
+            return Process.INVALID_UID;
+        }
+    }
+
     @GuardedBy("mLock")
     private void buildTvContentRatingSystemListLocked(int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
@@ -405,7 +417,7 @@
                 return;
             }
             if (mUserStates.contains(mCurrentUserId)) {
-                UserState userState = mUserStates.get(mCurrentUserId);
+                UserState userState = getUserStateLocked(mCurrentUserId);
                 List<SessionState> sessionStatesToRelease = new ArrayList<>();
                 for (SessionState sessionState : userState.sessionStateMap.values()) {
                     if (sessionState.session != null && !sessionState.isRecordingSession) {
@@ -474,7 +486,7 @@
 
     private void removeUser(int userId) {
         synchronized (mLock) {
-            UserState userState = mUserStates.get(userId);
+            UserState userState = getUserStateLocked(userId);
             if (userState == null) {
                 return;
             }
@@ -535,7 +547,7 @@
 
     @GuardedBy("mLock")
     private UserState getOrCreateUserStateLocked(int userId) {
-        UserState userState = mUserStates.get(userId);
+        UserState userState = getUserStateLocked(userId);
         if (userState == null) {
             userState = new UserState(mContext, userId);
             mUserStates.put(userId, userState);
@@ -715,7 +727,8 @@
     }
 
     @GuardedBy("mLock")
-    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+    @Nullable
+    private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
         SessionState sessionState = null;
         try {
             sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
@@ -738,6 +751,7 @@
             }
         }
         removeSessionStateLocked(sessionToken, userId);
+        return sessionState;
     }
 
     @GuardedBy("mLock")
@@ -908,6 +922,7 @@
             return;
         }
         inputState.info = inputInfo;
+        inputState.uid = getInputUid(inputInfo);
 
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
@@ -1248,7 +1263,22 @@
             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
                     userId, "createSession");
             final long identity = Binder.clearCallingIdentity();
-            // Generate a unique session id with a random UUID.
+            /**
+             * A randomly generated id for this this session.
+             *
+             * <p>This field contains no user or device reference and is large enough to be
+             * effectively globally unique.
+             *
+             * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+             * Inspect the code at:
+             *
+             * <ul>
+             *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+             *   <li>{@link #logTuneStateChanged}
+             *   <li>{@link TvInputManagerService.BinderService#createSession}
+             *   <li>{@link SessionState#sessionId}
+             * </ul>
+             */
             String uniqueSessionId = UUID.randomUUID().toString();
             try {
                 synchronized (mLock) {
@@ -1269,6 +1299,8 @@
                     TvInputInfo info = inputState.info;
                     ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                     if (serviceState == null) {
+                        int tisUid = PackageManager.getApplicationInfoAsUserCached(
+                                info.getComponent().getPackageName(), 0, resolvedUserId).uid;
                         serviceState = new ServiceState(info.getComponent(), resolvedUserId);
                         userState.serviceStateMap.put(info.getComponent(), serviceState);
                     }
@@ -1301,6 +1333,8 @@
                     } else {
                         updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
                     }
+                    logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED,
+                            sessionState, inputState);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1317,8 +1351,17 @@
                     userId, "releaseSession");
             final long identity = Binder.clearCallingIdentity();
             try {
+                SessionState sessionState = null;
+                UserState userState = null;
                 synchronized (mLock) {
-                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+                    sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+                    userState = getUserStateLocked(userId);
+                }
+                if (sessionState != null) {
+                    TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState,
+                            userState);
+                    logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED,
+                            sessionState, tvInputState);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1372,10 +1415,13 @@
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "setSurface");
             final long identity = Binder.clearCallingIdentity();
+            SessionState sessionState = null;
+            UserState userState = null;
             try {
                 synchronized (mLock) {
                     try {
-                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                        userState = getUserStateLocked(userId);
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
                         if (sessionState.hardwareSessionToken == null) {
                             getSessionLocked(sessionState).setSurface(surface);
@@ -1392,6 +1438,14 @@
                     // surface is not used in TvInputManagerService.
                     surface.release();
                 }
+                if (sessionState != null) {
+                    int state = surface == null
+                            ?
+                            FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED
+                            : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED;
+                    logTuneStateChanged(state, sessionState,
+                            TvInputManagerService.getTvInputState(sessionState, userState));
+                }
                 Binder.restoreCallingIdentity(identity);
             }
         }
@@ -1479,6 +1533,10 @@
                             return;
                         }
 
+                        logTuneStateChanged(
+                                FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED,
+                                sessionState,
+                                TvInputManagerService.getTvInputState(sessionState, userState));
                         // Log the start of watch.
                         SomeArgs args = SomeArgs.obtain();
                         args.arg1 = sessionState.componentName.getPackageName();
@@ -2302,6 +2360,16 @@
         }
     }
 
+    @Nullable
+    private static TvInputState getTvInputState(
+            SessionState sessionState,
+            @Nullable UserState userState) {
+        if (userState != null) {
+            return userState.inputMap.get(sessionState.inputId);
+        }
+        return null;
+    }
+
     @GuardedBy("mLock")
     private List<TunedInfo> getCurrentTunedInfosInternalLocked(
             UserState userState, int callingPid, int callingUid) {
@@ -2367,6 +2435,28 @@
         }
     }
 
+    /**
+     * Log Tune state changes to {@link FrameworkStatsLog}.
+     *
+     * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+     * Inspect the code at:
+     *
+     * <ul>
+     *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+     *   <li>{@link #logTuneStateChanged}
+     *   <li>{@link TvInputManagerService.BinderService#createSession}
+     *   <li>{@link SessionState#sessionId}
+     * </ul>
+     */
+    private void logTuneStateChanged(int state, SessionState sessionState,
+            @Nullable TvInputState inputState) {
+        // TODO(b/173536904): log input type and id
+        FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED,
+                new int[]{sessionState.callingUid,
+                        inputState == null ? Process.INVALID_UID : inputState.uid},
+                new String[]{"tif_player", "tv_input_service"}, state, sessionState.sessionId);
+    }
+
     private static final class UserState {
         // A mapping from the TV input id to its TvInputState.
         private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -2464,10 +2554,25 @@
     }
 
     private static final class TvInputState {
-        // A TvInputInfo object which represents the TV input.
+
+        /** A TvInputInfo object which represents the TV input. */
         private TvInputInfo info;
 
-        // The state of TV input. Connected by default.
+        /**
+         * The kernel user-ID that has been assigned to the application the TvInput is a part of.
+         *
+         * <p>
+         * Currently this is not a unique ID (multiple applications can have
+         * the same uid).
+         */
+        private int uid;
+
+        /**
+         * The state of TV input.
+         *
+         * <p>
+         * Connected by default
+         */
         private int state = INPUT_STATE_CONNECTED;
 
         @Override
@@ -2478,6 +2583,23 @@
 
     private final class SessionState implements IBinder.DeathRecipient {
         private final String inputId;
+
+        /**
+         * A randomly generated id for this this session.
+         *
+         * <p>This field contains no user or device reference and is large enough to be
+         * effectively globally unique.
+         *
+         * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+         * Inspect the code at:
+         *
+         * <ul>
+         *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+         *   <li>{@link #logTuneStateChanged}
+         *   <li>{@link TvInputManagerService.BinderService#createSession}
+         *   <li>{@link SessionState#sessionId}
+         * </ul>
+         */
         private final String sessionId;
         private final ComponentName componentName;
         private final boolean isRecordingSession;
@@ -2545,7 +2667,7 @@
                 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
             }
             synchronized (mLock) {
-                UserState userState = mUserStates.get(mUserId);
+                UserState userState = getUserStateLocked(mUserId);
                 if (userState == null) {
                     // The user was removed while connecting.
                     mContext.unbindService(this);
@@ -2815,8 +2937,13 @@
                 if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
+                TvInputState tvInputState = getTvInputState(mSessionState,
+                        getUserStateLocked(mCurrentUserId));
                 try {
                     mSessionState.client.onVideoAvailable(mSessionState.seq);
+                    logTuneStateChanged(
+                            FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE,
+                            mSessionState, tvInputState);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onVideoAvailable", e);
                 }
@@ -2832,8 +2959,20 @@
                 if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
+                TvInputState tvInputState = getTvInputState(mSessionState,
+                        getUserStateLocked(mCurrentUserId));
                 try {
                     mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
+                    int loggedReason = reason + FrameworkStatsLog
+                            .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+                    if (loggedReason < FrameworkStatsLog
+                            .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN
+                            || loggedReason > FrameworkStatsLog
+                            .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) {
+                        loggedReason = FrameworkStatsLog
+                                .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+                    }
+                    logTuneStateChanged(loggedReason, mSessionState, tvInputState);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onVideoUnavailable", e);
                 }
@@ -3019,6 +3158,10 @@
         }
     }
 
+    private UserState getUserStateLocked(int userId) {
+        return mUserStates.get(userId);
+    }
+
     private static final class WatchLogHandler extends Handler {
         // There are only two kinds of watch events that can happen on the system:
         // 1. The current TV input session is tuned to a new channel.
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
new file mode 100644
index 0000000..e1feb5a
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -0,0 +1,100 @@
+/*
+ * 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.vcn;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Handler;
+import android.os.ParcelUuid;
+
+import java.util.Objects;
+
+/**
+ * Tracks a set of Networks underpinning a VcnGatewayConnection.
+ *
+ * <p>A single UnderlyingNetworkTracker is built to serve a SINGLE VCN Gateway Connection, and MUST
+ * be torn down with the VcnGatewayConnection in order to ensure underlying networks are allowed to
+ * be reaped.
+ *
+ * @hide
+ */
+public class UnderlyingNetworkTracker extends Handler {
+    @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
+
+    @NonNull private final VcnContext mVcnContext;
+    @NonNull private final ParcelUuid mSubscriptionGroup;
+    @NonNull private final UnderlyingNetworkTrackerCallback mCb;
+    @NonNull private final Dependencies mDeps;
+
+    public UnderlyingNetworkTracker(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull UnderlyingNetworkTrackerCallback cb) {
+        this(vcnContext, subscriptionGroup, cb, new Dependencies());
+    }
+
+    private UnderlyingNetworkTracker(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull UnderlyingNetworkTrackerCallback cb,
+            @NonNull Dependencies deps) {
+        super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
+        mVcnContext = vcnContext;
+        mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mCb = Objects.requireNonNull(cb, "Missing cb");
+        mDeps = Objects.requireNonNull(deps, "Missing deps");
+    }
+
+    /** Tears down this Tracker, and releases all underlying network requests. */
+    public void teardown() {}
+
+    /** An record of a single underlying network, caching relevant fields. */
+    public static class UnderlyingNetworkRecord {
+        @NonNull public final Network network;
+        @NonNull public final NetworkCapabilities networkCapabilities;
+        @NonNull public final LinkProperties linkProperties;
+        public final boolean blocked;
+
+        private UnderlyingNetworkRecord(
+                @NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities,
+                @NonNull LinkProperties linkProperties,
+                boolean blocked) {
+            this.network = network;
+            this.networkCapabilities = networkCapabilities;
+            this.linkProperties = linkProperties;
+            this.blocked = blocked;
+        }
+    }
+
+    /** Callbacks for being notified of the changes in, or to the selected underlying network. */
+    public interface UnderlyingNetworkTrackerCallback {
+        /**
+         * Fired when a new underlying network is selected, or properties have changed.
+         *
+         * <p>This callback does NOT signal a mobility event.
+         *
+         * @param underlying The details of the new underlying network
+         */
+        void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying);
+    }
+
+    private static class Dependencies {}
+}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
new file mode 100644
index 0000000..d51d16b
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -0,0 +1,96 @@
+/*
+ * 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.vcn;
+
+import android.annotation.NonNull;
+import android.net.NetworkRequest;
+import android.net.vcn.VcnConfig;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ParcelUuid;
+
+import java.util.Objects;
+
+/**
+ * Represents an single instance of a VCN.
+ *
+ * <p>Each Vcn instance manages all tunnels for a given subscription group, including per-capability
+ * networks, network selection, and multi-homing.
+ *
+ * @hide
+ */
+public class Vcn extends Handler {
+    private static final String TAG = Vcn.class.getSimpleName();
+
+    @NonNull private final VcnContext mVcnContext;
+    @NonNull private final ParcelUuid mSubscriptionGroup;
+    @NonNull private final Dependencies mDeps;
+
+    @NonNull private VcnConfig mConfig;
+
+    public Vcn(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull VcnConfig config) {
+        this(vcnContext, subscriptionGroup, config, new Dependencies());
+    }
+
+    private Vcn(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull VcnConfig config,
+            @NonNull Dependencies deps) {
+        super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
+        mVcnContext = vcnContext;
+        mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+        mConfig = Objects.requireNonNull(config, "Missing config");
+    }
+
+    /** Asynchronously updates the configuration and triggers a re-evaluation of Networks */
+    public void updateConfig(@NonNull VcnConfig config) {
+        Objects.requireNonNull(config, "Missing config");
+        // TODO: Proxy to handler, and make config there.
+    }
+
+    /** Asynchronously tears down this Vcn instance, along with all tunnels and Networks */
+    public void teardown() {
+        // TODO: Proxy to handler, and teardown there.
+    }
+
+    /** Notifies this Vcn instance of a new NetworkRequest */
+    public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
+        Objects.requireNonNull(request, "Missing request");
+
+        // TODO: Proxy to handler, and handle there.
+    }
+
+    @Override
+    public void handleMessage(@NonNull Message msg) {
+        // TODO: Do something
+    }
+
+    /** Retrieves the network score for a VCN Network */
+    private int getNetworkScore() {
+        // TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
+        //                               subGrp" value
+        return 52;
+    }
+
+    private static class Dependencies {}
+}
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
new file mode 100644
index 0000000..8ab52931
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -0,0 +1,60 @@
+/*
+ * 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.vcn;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.server.VcnManagementService.VcnNetworkProvider;
+
+import java.util.Objects;
+
+/**
+ * A simple class to pass around context information.
+ *
+ * @hide
+ */
+public class VcnContext {
+    @NonNull private final Context mContext;
+    @NonNull private final Looper mLooper;
+    @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+
+    public VcnContext(
+            @NonNull Context context,
+            @NonNull Looper looper,
+            @NonNull VcnNetworkProvider vcnNetworkProvider) {
+        mContext = Objects.requireNonNull(context, "Missing context");
+        mLooper = Objects.requireNonNull(looper, "Missing looper");
+        mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
+    }
+
+    @NonNull
+    public Context getContext() {
+        return mContext;
+    }
+
+    @NonNull
+    public Looper getLooper() {
+        return mLooper;
+    }
+
+    @NonNull
+    public VcnNetworkProvider getVcnNetworkProvider() {
+        return mVcnNetworkProvider;
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
new file mode 100644
index 0000000..49c9b32
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.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 com.android.server.vcn;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.os.Handler;
+import android.os.ParcelUuid;
+
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+
+import java.util.Objects;
+
+/**
+ * A single VCN Gateway Connection, providing a single public-facing VCN network.
+ *
+ * <p>This class handles mobility events, performs retries, and tracks safe-mode conditions.
+ *
+ * @hide
+ */
+public class VcnGatewayConnection extends Handler implements UnderlyingNetworkTrackerCallback {
+    private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+
+    @NonNull private final VcnContext mVcnContext;
+    @NonNull private final ParcelUuid mSubscriptionGroup;
+    @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+    @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+    @NonNull private final Dependencies mDeps;
+
+    public VcnGatewayConnection(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull VcnGatewayConnectionConfig connectionConfig) {
+        this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
+    }
+
+    private VcnGatewayConnection(
+            @NonNull VcnContext vcnContext,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull Dependencies deps) {
+        super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
+        mVcnContext = vcnContext;
+        mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+        mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+        mUnderlyingNetworkTracker =
+                mDeps.newUnderlyingNetworkTracker(mVcnContext, subscriptionGroup, this);
+    }
+
+    /** Tears down this GatewayConnection, and any resources used */
+    public void teardown() {
+        mUnderlyingNetworkTracker.teardown();
+    }
+
+    private static class Dependencies {
+        public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
+                VcnContext vcnContext,
+                ParcelUuid subscriptionGroup,
+                UnderlyingNetworkTrackerCallback callback) {
+            return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
+        }
+    }
+
+    @Override
+    public void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying) {}
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 329589e..e07540a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -78,7 +78,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
@@ -107,7 +106,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d8d1a65..b4ca7c5 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,8 +3,10 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WaitResult.INVALID_DELAY;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH;
 import static android.app.WaitResult.LAUNCH_STATE_WARM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -130,7 +132,6 @@
      * transition, in the case the launch is standalone (e.g. from recents).
      */
     private static final int IGNORE_CALLER = -1;
-    private static final int INVALID_DELAY = -1;
 
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
@@ -220,6 +221,8 @@
         boolean mLoggedStartingWindowDrawn;
         /** If the any app transitions have been logged as starting. */
         boolean mLoggedTransitionStarting;
+        /** Whether any activity belonging to this transition has relaunched. */
+        boolean mRelaunched;
 
         /** Non-null if the application has reported drawn but its window hasn't. */
         @Nullable Runnable mPendingFullyDrawn;
@@ -351,6 +354,7 @@
          */
         final int windowsFullyDrawnDelayMs;
         final int activityRecordIdHashCode;
+        final boolean relaunched;
 
         private TransitionInfoSnapshot(TransitionInfo info) {
             this(info, info.mLastLaunchedActivity, INVALID_DELAY);
@@ -379,6 +383,7 @@
             launchedActivityShortComponentName = launchedActivity.shortComponentName;
             activityRecordIdHashCode = System.identityHashCode(launchedActivity);
             this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
+            relaunched = info.mRelaunched;
         }
 
         @WaitResult.LaunchState int getLaunchState() {
@@ -386,7 +391,7 @@
                 case TYPE_TRANSITION_WARM_LAUNCH:
                     return LAUNCH_STATE_WARM;
                 case TYPE_TRANSITION_HOT_LAUNCH:
-                    return LAUNCH_STATE_HOT;
+                    return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT;
                 case TYPE_TRANSITION_COLD_LAUNCH:
                     return LAUNCH_STATE_COLD;
                 default:
@@ -673,6 +678,13 @@
         }
     }
 
+    void notifyActivityRelaunched(ActivityRecord r) {
+        final TransitionInfo info = getActiveTransitionInfo(r);
+        if (info != null) {
+            info.mRelaunched = true;
+        }
+    }
+
     /** Makes sure that the reference to the removed activity is cleared. */
     void notifyActivityRemoved(@NonNull ActivityRecord r) {
         mLastTransitionInfo.remove(r);
@@ -800,13 +812,13 @@
                 FrameworkStatsLog.APP_START_CANCELED,
                 activity.info.applicationInfo.uid,
                 activity.packageName,
-                convertAppStartTransitionType(type),
+                getAppStartTransitionType(type, info.mRelaunched),
                 activity.info.name);
         if (DEBUG_METRICS) {
             Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
                     activity.info.applicationInfo.uid,
                     activity.packageName,
-                    convertAppStartTransitionType(type),
+                    getAppStartTransitionType(type, info.mRelaunched),
                     activity.info.name));
         }
     }
@@ -871,7 +883,7 @@
                 FrameworkStatsLog.APP_START_OCCURRED,
                 info.applicationInfo.uid,
                 info.packageName,
-                convertAppStartTransitionType(info.type),
+                getAppStartTransitionType(info.type, info.relaunched),
                 info.launchedActivityName,
                 info.launchedActivityLaunchedFromPackage,
                 isInstantApp,
@@ -891,7 +903,7 @@
             Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
                     info.applicationInfo.uid,
                     info.packageName,
-                    convertAppStartTransitionType(info.type),
+                    getAppStartTransitionType(info.type, info.relaunched),
                     info.launchedActivityName,
                     info.launchedActivityLaunchedFromPackage));
         }
@@ -918,7 +930,7 @@
         Log.i(TAG, sb.toString());
     }
 
-    private int convertAppStartTransitionType(int tronType) {
+    private static int getAppStartTransitionType(int tronType, boolean relaunched) {
         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
             return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
         }
@@ -926,17 +938,13 @@
             return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM;
         }
         if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
-            return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
+            return relaunched
+                    ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH
+                    : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
         }
         return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
     }
 
-    /** @return the last known window drawn delay of the given activity. */
-    int getLastDrawnDelayMs(ActivityRecord r) {
-        final TransitionInfo info = mLastTransitionInfo.get(r);
-        return info != null ? info.mWindowsDrawnDelayMs : INVALID_DELAY;
-    }
-
     /** @see android.app.Activity#reportFullyDrawn */
     TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
             boolean restoredFromBundle) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d8b750e..c2016de 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -103,9 +103,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.TransitionOldType;
 
@@ -150,7 +150,6 @@
 import static com.android.server.wm.ActivityRecordProto.VISIBLE_REQUESTED;
 import static com.android.server.wm.ActivityRecordProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
 import static com.android.server.wm.ActivityRecordProto.WINDOW_TOKEN;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -177,6 +176,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -226,7 +226,7 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
-import android.app.WaitResult.LaunchState;
+import android.app.WaitResult;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
@@ -1558,10 +1558,6 @@
             }
         }
 
-        // Application tokens start out hidden.
-        setVisible(false);
-        mVisibleRequested = false;
-
         ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
                 ColorDisplayService.ColorDisplayServiceInternal.class);
         cds.attachColorTransformController(packageName, mUserId,
@@ -2609,7 +2605,6 @@
                 if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
                     Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
                 }
-                mDisplayContent.prepareAppTransitionOld(transit, false);
                 mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
 
                 // When finishing the activity preemptively take the snapshot before the app window
@@ -2693,7 +2688,6 @@
 
     private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) {
         final DisplayContent dc = mDisplayContent;
-        dc.prepareAppTransitionOld(transit, false);
         dc.prepareAppTransition(TRANSIT_CLOSE);
         setVisibility(false);
         dc.executeAppTransition();
@@ -3132,6 +3126,7 @@
     }
 
     void finishRelaunching() {
+        mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
         unfreezeBounds();
 
         if (mPendingRelaunchCount > 0) {
@@ -3508,7 +3503,7 @@
                 }
                 if (fromActivity.isVisible()) {
                     setVisible(true);
-                    mVisibleRequested = true;
+                    setVisibleRequested(true);
                     mVisibleSetFromTransferredStartingWindow = true;
                 }
                 setClientVisible(fromActivity.mClientVisible);
@@ -4124,11 +4119,28 @@
     void setVisible(boolean visible) {
         if (visible != mVisible) {
             mVisible = visible;
+            if (app != null) {
+                mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
+            }
             scheduleAnimation();
         }
     }
 
     /**
+     * This is the only place that writes {@link #mVisibleRequested} (except unit test). The caller
+     * outside of this class should use {@link #setVisibility}.
+     */
+    private void setVisibleRequested(boolean visible) {
+        if (visible == mVisibleRequested) {
+            return;
+        }
+        mVisibleRequested = visible;
+        if (app != null) {
+            mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
+        }
+    }
+
+    /**
      * Set visibility on this {@link ActivityRecord}
      *
      * <p class="note"><strong>Note: </strong>This function might not update the visibility of
@@ -4191,7 +4203,7 @@
         displayContent.mOpeningApps.remove(this);
         displayContent.mClosingApps.remove(this);
         waitingToShow = false;
-        mVisibleRequested = visible;
+        setVisibleRequested(visible);
         mLastDeferHidingClient = deferHidingClient;
 
         if (!visible) {
@@ -4274,14 +4286,14 @@
                 displayContent.mClosingApps.add(this);
                 mEnteringAnimation = false;
             }
-            if (appTransition.getAppTransitionOld() == TRANSIT_OLD_TASK_OPEN_BEHIND) {
+            if ((appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0) {
                 // We're launchingBehind, add the launching activity to mOpeningApps.
                 final WindowState win = getDisplayContent().findFocusedWindow();
                 if (win != null) {
                     final ActivityRecord focusedActivity = win.mActivityRecord;
                     if (focusedActivity != null) {
                         ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
-                                "TRANSIT_TASK_OPEN_BEHIND,  adding %s to mOpeningApps",
+                                "TRANSIT_FLAG_OPEN_BEHIND,  adding %s to mOpeningApps",
                                 focusedActivity);
 
                         // Force animation to be loaded.
@@ -4297,7 +4309,7 @@
     }
 
     @Override
-    boolean applyAnimation(LayoutParams lp, int transit, boolean enter,
+    boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
             boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
         if (mUseTransferredAnimation) {
             return false;
@@ -4333,7 +4345,7 @@
                     ANIMATION_TYPE_APP_TRANSITION));
         }
         setVisible(visible);
-        mVisibleRequested = visible;
+        setVisibleRequested(visible);
         if (!visible) {
             stopFreezingScreen(true, true);
         } else {
@@ -4521,7 +4533,7 @@
             detachChildren();
         }
         if (app != null) {
-            app.computeProcessActivityState();
+            mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
         }
 
         switch (state) {
@@ -5437,14 +5449,15 @@
                 .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
         final boolean validInfo = info != null;
         final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
-        final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1;
+        final @WaitResult.LaunchState int launchState =
+                validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN;
         // The activity may have been requested to be invisible (another activity has been launched)
         // so there is no valid info. But if it is the current top activity (e.g. sleeping), the
         // invalid state is still reported to make sure the waiting result is notified.
         if (validInfo || this == getDisplayArea().topRunningActivity()) {
             mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
                     windowsDrawnDelayMs, launchState);
-            mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+            mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState);
         }
         finishLaunchTickingLocked();
         if (task != null) {
@@ -6312,7 +6325,7 @@
         mThumbnail = null;
     }
 
-    public int getTransit() {
+    public @TransitionOldType int getTransit() {
         return mTransit;
     }
 
@@ -6395,13 +6408,10 @@
             // The app is just becoming visible, and the parent Task has updated with the
             // orientation request. Update the size compat mode.
             updateSizeCompatMode();
-            if (task.isOrganized()) {
-                // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure
-                // that WM Shell is called when an activity becomes visible. Without this, WM Core
-                // will handle positioning instead of WM Shell when an app is reopened.
-                mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                        task, /* force= */ true);
-            }
+            // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure
+            // that WM Shell is called when an activity becomes visible. Without this, WM Core
+            // will handle positioning instead of WM Shell when an app is reopened.
+            task.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
         }
     }
 
@@ -7165,11 +7175,7 @@
             } else {
                 mRelaunchReason = RELAUNCH_REASON_NONE;
             }
-            if (!attachedToProcess()) {
-                ProtoLog.v(WM_DEBUG_CONFIGURATION,
-                        "Config is destroying non-running %s", this);
-                destroyImmediately("config");
-            } else if (mState == PAUSING) {
+            if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
                 ProtoLog.v(WM_DEBUG_CONFIGURATION,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f472a5d..acee7b2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -28,8 +28,6 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WaitResult.LAUNCH_STATE_COLD;
-import static android.app.WaitResult.LAUNCH_STATE_HOT;
 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;
@@ -819,8 +817,6 @@
                 break;
             }
             case START_TASK_TO_FRONT: {
-                mRequest.waitResult.launchState =
-                        r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD;
                 // ActivityRecord may represent a different activity, but it should not be
                 // in the resumed state.
                 if (r.nowVisible && r.isState(RESUMED)) {
@@ -1285,6 +1281,15 @@
             return false;
         }
 
+        // IME should always be allowed to start activity, like IME settings.
+        final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
+        if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
+            if (DEBUG_ACTIVITY_STARTS) {
+                Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
+            }
+            return false;
+        }
+
         // App switching will be allowed if BAL app switching flag is not enabled, or if
         // its app switching rule allows it.
         // This is used to block background activity launch even if the app is still
@@ -1292,9 +1297,8 @@
         final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
 
         // don't abort if the callingUid has a visible window or is a persistent system process
-        final int callingUidProcState = mService.getUidState(callingUid);
-        final boolean callingUidHasAnyVisibleWindow =
-                mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
+        final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
+        final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
         final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
                 || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
                 || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
@@ -1312,10 +1316,10 @@
         // take realCallingUid into consideration
         final int realCallingUidProcState = (callingUid == realCallingUid)
                 ? callingUidProcState
-                : mService.getUidState(realCallingUid);
+                : mService.mActiveUids.getUidState(realCallingUid);
         final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                 ? callingUidHasAnyVisibleWindow
-                : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
+                : mService.hasActiveVisibleWindow(realCallingUid);
         final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
                 ? isCallingUidForeground
                 : realCallingUidHasAnyVisibleWindow
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index f649a2f..a5df2a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -514,7 +514,6 @@
 
     public abstract void onUidActive(int uid, int procState);
     public abstract void onUidInactive(int uid);
-    public abstract void onActiveUidsCleared();
     public abstract void onUidProcStateChanged(int uid, int procState);
 
     public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 43326bd..27faf13 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -67,7 +67,6 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -95,10 +94,6 @@
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
 import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
 import static com.android.server.am.EventLogTags.writeConfigurationChanged;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
@@ -117,6 +112,10 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -164,7 +163,6 @@
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.EnterPipRequestedItem;
 import android.app.usage.UsageStatsManagerInternal;
-import android.compat.annotation.ChangeId;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -392,7 +390,7 @@
     private UserManagerService mUserManager;
     private AppOpsManager mAppOpsManager;
     /** All active uids in the system. */
-    private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
+    final MirrorActiveUids mActiveUids = new MirrorActiveUids();
     private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>();
     /** All processes currently running that might have a window organized by name. */
     final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
@@ -3648,7 +3646,7 @@
      */
     private static int checkCallingPermission(String permission) {
         return checkPermission(
-                permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
+                permission, Binder.getCallingPid(), Binder.getCallingUid());
     }
 
     /** This can be called with or without the global lock held. */
@@ -4958,6 +4956,8 @@
             printedAnything = true;
             mTaskSupervisor.dump(pw, "  ");
             mTaskOrganizerController.dump(pw, "  ");
+            mVisibleActivityProcessTracker.dump(pw, "  ");
+            mActiveUids.dump(pw, "  ");
         }
 
         if (!printedAnything) {
@@ -5988,13 +5988,12 @@
         return null;
     }
 
-    int getUidState(int uid) {
-        return mActiveUids.getUidState(uid);
-    }
-
-    boolean isUidForeground(int uid) {
-        // A uid is considered to be foreground if it has a visible non-toast window.
-        return mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
+    /** A uid is considered to be foreground if it has a visible non-toast window. */
+    boolean hasActiveVisibleWindow(int uid) {
+        if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) {
+            return true;
+        }
+        return mActiveUids.hasNonAppVisibleWindow(uid);
     }
 
     boolean isDeviceOwner(int uid) {
@@ -6248,7 +6247,6 @@
                 }
                 final boolean wasTransitionSet = dc.mAppTransition.isTransitionSet();
                 if (!wasTransitionSet) {
-                    dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false /* alwaysKeepCurrent */);
                     dc.prepareAppTransition(TRANSIT_NONE);
                 }
                 mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
@@ -7286,12 +7284,6 @@
 
         @HotPath(caller = HotPath.OOM_ADJUSTMENT)
         @Override
-        public void onActiveUidsCleared() {
-            mActiveUids.onActiveUidsCleared();
-        }
-
-        @HotPath(caller = HotPath.OOM_ADJUSTMENT)
-        @Override
         public void onUidProcStateChanged(int uid, int procState) {
             mActiveUids.onUidProcStateChanged(uid, procState);
         }
@@ -7435,9 +7427,7 @@
 
         @Override
         public boolean isUidForeground(int uid) {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.isUidForeground(uid);
-            }
+            return ActivityTaskManagerService.this.hasActiveVisibleWindow(uid);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e91a6d8..37f04ce 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -254,6 +254,12 @@
     private LaunchParamsController mLaunchParamsController;
 
     /**
+     * The processes with changed states that should eventually call
+     * {@link WindowProcessController#computeProcessActivityState}.
+     */
+    private final ArrayList<WindowProcessController> mActivityStateChangedProcs = new ArrayList<>();
+
+    /**
      * Maps the task identifier that activities are currently being started in to the userId of the
      * task. Each time a new task is created, the entry for the userId of the task is incremented
      */
@@ -553,14 +559,16 @@
         // down to the max limit while they are still waiting to finish.
         mFinishingActivities.remove(r);
 
-        stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY);
+        stopWaitingForActivityVisible(r);
     }
 
+    /** There is no valid launch time, just stop waiting. */
     void stopWaitingForActivityVisible(ActivityRecord r) {
-        stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
+        stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN);
     }
 
-    void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
+    void stopWaitingForActivityVisible(ActivityRecord r, long totalTime,
+            @WaitResult.LaunchState int launchState) {
         boolean changed = false;
         for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
             final WaitInfo w = mWaitingForActivityVisible.get(i);
@@ -570,6 +578,7 @@
                 result.timeout = false;
                 result.who = w.getComponent();
                 result.totalTime = totalTime;
+                result.launchState = launchState;
                 mWaitingForActivityVisible.remove(w);
             }
         }
@@ -2307,6 +2316,9 @@
     /** Ends a batch of visibility updates. */
     void endActivityVisibilityUpdate() {
         mVisibilityTransactionDepth--;
+        if (mVisibilityTransactionDepth == 0) {
+            computeProcessActivityStateBatch();
+        }
     }
 
     /** Returns {@code true} if the caller is on the path to update visibility. */
@@ -2315,6 +2327,34 @@
     }
 
     /**
+     * Called when the state or visibility of an attached activity is changed.
+     *
+     * @param wpc The process who owns the activity.
+     * @param forceBatch Whether to put the changed record to a pending list. If the caller is not
+     *                   in the path of visibility update ({@link #inActivityVisibilityUpdate}), it
+     *                   must call {@link #computeProcessActivityStateBatch} manually.
+     */
+    void onProcessActivityStateChanged(WindowProcessController wpc, boolean forceBatch) {
+        if (forceBatch || inActivityVisibilityUpdate()) {
+            if (!mActivityStateChangedProcs.contains(wpc)) {
+                mActivityStateChangedProcs.add(wpc);
+            }
+            return;
+        }
+        wpc.computeProcessActivityState();
+    }
+
+    void computeProcessActivityStateBatch() {
+        if (mActivityStateChangedProcs.isEmpty()) {
+            return;
+        }
+        for (int i = mActivityStateChangedProcs.size() - 1; i >= 0; i--) {
+            mActivityStateChangedProcs.get(i).computeProcessActivityState();
+        }
+        mActivityStateChangedProcs.clear();
+    }
+
+    /**
      * Begin deferring resume to avoid duplicate resumes in one pass.
      */
     void beginDeferResume() {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e146ada..758aaa0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
@@ -115,7 +116,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -184,7 +184,6 @@
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
 
-    private @TransitionOldType int mNextAppTransitionOld = TRANSIT_OLD_UNSET;
     private @TransitionFlags int mNextAppTransitionFlags = 0;
     private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
     private @TransitionOldType int mLastUsedAppTransition = TRANSIT_OLD_UNSET;
@@ -330,46 +329,20 @@
     }
 
     boolean isTransitionSet() {
-        return mNextAppTransitionOld != TRANSIT_OLD_UNSET || !mNextAppTransitionRequests.isEmpty();
-    }
-
-    // TODO(new-app-tranistion): Remove this after migrating to new app transition system.
-    boolean isTransitionOldEqual(@TransitionOldType int transit) {
-        return mNextAppTransitionOld == transit;
+        return !mNextAppTransitionRequests.isEmpty();
     }
 
     boolean isUnoccluding() {
-        return WindowManagerService.sUseNewAppTransit
-                ? mNextAppTransitionRequests.contains(TRANSIT_OLD_KEYGUARD_UNOCCLUDE)
-                : mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+        return mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_UNOCCLUDE);
     }
 
-    @TransitionOldType
-    int getAppTransitionOld() {
-        return mNextAppTransitionOld;
-     }
-
     boolean transferFrom(AppTransition other) {
-        prepareAppTransitionOld(other.getAppTransitionOld(), true /* alwaysKeepCurrent */,
-                0 /* flags */, false /* forceOverride */);
         mNextAppTransitionRequests.addAll(other.mNextAppTransitionRequests);
         return prepare();
     }
 
-    private void setAppTransitionOld(@TransitionOldType int transit, int flags) {
-        mNextAppTransitionOld = transit;
-        mNextAppTransitionFlags |= flags;
-        setLastAppTransition(TRANSIT_OLD_UNSET, null, null, null);
-        updateBooster();
-        if (isTransitionSet()) {
-            removeAppTransitionTimeoutCallbacks();
-            mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable,
-                    APP_TRANSITION_TIMEOUT_MS);
-        }
-    }
-
-    void setLastAppTransition(int transit, ActivityRecord openingApp, ActivityRecord closingApp,
-            ActivityRecord changingApp) {
+    void setLastAppTransition(@TransitionOldType int transit, ActivityRecord openingApp,
+            ActivityRecord closingApp, ActivityRecord changingApp) {
         mLastUsedAppTransition = transit;
         mLastOpeningApp = "" + openingApp;
         mLastClosingApp = "" + closingApp;
@@ -460,8 +433,7 @@
      * @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
      *         layout pass needs to be done
      */
-    int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
-        mNextAppTransitionOld = TRANSIT_OLD_UNSET;
+    int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) {
         mNextAppTransitionFlags = 0;
         mNextAppTransitionRequests.clear();
         setAppTransitionState(APP_STATE_RUNNING);
@@ -469,7 +441,8 @@
                 topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
         final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
 
-        int redoLayout = notifyAppTransitionStartingLocked(transit,
+        int redoLayout = notifyAppTransitionStartingLocked(
+                AppTransition.isKeyguardGoingAwayTransitOld(transit),
                 topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
                 topOpeningAnim != null
                         ? topOpeningAnim.getStatusBarTransitionsStartTime()
@@ -494,7 +467,9 @@
     }
 
     void freeze() {
-        final int transit = mNextAppTransitionOld;
+        final boolean keyguardGoingAway = mNextAppTransitionRequests.contains(
+                TRANSIT_KEYGUARD_GOING_AWAY);
+
         // The RemoteAnimationControl didn't register AppTransitionListener and
         // only initialized the finish and timeout callback when goodToGo().
         // So cancel the remote animation here to prevent the animation can't do
@@ -502,10 +477,10 @@
         if (mRemoteAnimationController != null) {
             mRemoteAnimationController.cancelAnimation("freeze");
         }
-        setAppTransitionOld(TRANSIT_OLD_UNSET, 0 /* flags */);
+        mNextAppTransitionRequests.clear();
         clear();
         setReady();
-        notifyAppTransitionCancelledLocked(transit);
+        notifyAppTransitionCancelledLocked(keyguardGoingAway);
     }
 
     private void setAppTransitionState(int state) {
@@ -524,7 +499,7 @@
 
     private boolean needsBoosting() {
         final boolean recentsAnimRunning = mService.getRecentsAnimationController() != null;
-        return mNextAppTransitionOld != TRANSIT_OLD_UNSET
+        return !mNextAppTransitionRequests.isEmpty()
                 || mAppTransitionState == APP_STATE_READY
                 || mAppTransitionState == APP_STATE_RUNNING
                 || recentsAnimRunning;
@@ -550,9 +525,9 @@
         }
     }
 
-    private void notifyAppTransitionCancelledLocked(int transit) {
+    private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) {
         for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).onAppTransitionCancelledLocked(transit);
+            mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway);
         }
     }
 
@@ -562,12 +537,12 @@
         }
     }
 
-    private int notifyAppTransitionStartingLocked(int transit, long duration,
+    private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
             long statusBarAnimationStartTime, long statusBarAnimationDuration) {
         int redoLayout = 0;
         for (int i = 0; i < mListeners.size(); i++) {
-            redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, duration,
-                    statusBarAnimationStartTime, statusBarAnimationDuration);
+            redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+                    duration, statusBarAnimationStartTime, statusBarAnimationDuration);
         }
         return redoLayout;
     }
@@ -1578,7 +1553,7 @@
                 && !mNextAppTransitionOverrideRequested
                 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
                 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL
-                && mNextAppTransitionOld != TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+                && !mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY);
     }
 
     RemoteAnimationController getRemoteAnimationController() {
@@ -1616,7 +1591,7 @@
         }
 
         Animation a;
-        if (isKeyguardGoingAwayTransit(transit) && enter) {
+        if (isKeyguardGoingAwayTransitOld(transit) && enter) {
             a = loadKeyguardExitAnimation(transit);
         } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
             a = null;
@@ -1645,9 +1620,8 @@
         } else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) {
             a = createRelaunchAnimation(frame, insets);
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
-                    "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", a,
-                    mNextAppTransitionOld, appTransitionOldToString(transit),
-                    Debug.getCallers(3));
+                    "applyAnimation: anim=%s transit=%s Callers=%s", a,
+                    appTransitionOldToString(transit), Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
             a = loadAnimationRes(mNextAppTransitionPackage, enter ?
                     mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1799,14 +1773,12 @@
     int getAppStackClipMode() {
         return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
                 || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY)
-                || mNextAppTransitionOld == TRANSIT_OLD_ACTIVITY_RELAUNCH
                 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
-                || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY
-                || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
                 ? STACK_CLIP_NONE
                 : STACK_CLIP_AFTER_ANIM;
     }
 
+    @TransitionFlags
     public int getTransitFlags() {
         return mNextAppTransitionFlags;
     }
@@ -2001,9 +1973,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("mNextAppTransition=");
-        sb.append(appTransitionOldToString(mNextAppTransitionOld));
-        sb.append(", mNextAppTransitionRequests=[");
+        sb.append("mNextAppTransitionRequests=[");
 
         boolean separator = false;
         for (Integer transit : mNextAppTransitionRequests) {
@@ -2194,6 +2164,8 @@
                 "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION"));
         sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED,
                 "TRANSIT_FLAG_APP_CRASHED"));
+        sFlagToString.add(new Pair<>(TRANSIT_FLAG_OPEN_BEHIND,
+                "TRANSIT_FLAG_OPEN_BEHIND"));
     }
 
     /**
@@ -2291,47 +2263,6 @@
         mCurrentUserId = newUserId;
     }
 
-    /**
-     * @return true if transition is not running and should not be skipped, false if transition is
-     *         already running
-     */
-    boolean prepareAppTransitionOld(@TransitionOldType int transit, boolean alwaysKeepCurrent,
-            @TransitionFlags int flags, boolean forceOverride) {
-        if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
-            return false;
-        }
-        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d "
-                        + "Callers=%s",
-                appTransitionOldToString(transit), this, alwaysKeepCurrent,
-                mDisplayContent.getDisplayId(), Debug.getCallers(5));
-        final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransitionOld)
-                && transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
-        if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
-                || mNextAppTransitionOld == TRANSIT_OLD_NONE || allowSetCrashing) {
-            setAppTransitionOld(transit, flags);
-        }
-        // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
-        // relies on the fact that we always execute a Keyguard transition after preparing one. We
-        // also don't want to change away from a crashing transition.
-        else if (!alwaysKeepCurrent && !isKeyguardTransit(mNextAppTransitionOld)
-                && mNextAppTransitionOld != TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
-            if (transit == TRANSIT_OLD_TASK_OPEN && isTransitionOldEqual(TRANSIT_OLD_TASK_CLOSE)) {
-                // Opening a new task always supersedes a close for the anim.
-                setAppTransitionOld(transit, flags);
-            } else if (transit == TRANSIT_OLD_ACTIVITY_OPEN
-                    && isTransitionOldEqual(TRANSIT_OLD_ACTIVITY_CLOSE)) {
-                // Opening a new activity always supersedes a close for the anim.
-                setAppTransitionOld(transit, flags);
-            } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransitionOld)) {
-                // Task animations always supersede activity animations, because if we have both, it
-                // usually means that activity transition were just trampoline activities.
-                setAppTransitionOld(transit, flags);
-            }
-        }
-        return prepare();
-    }
-
     boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
         if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
             return false;
@@ -2349,39 +2280,39 @@
      * @return true if {@param transit} is representing a transition in which Keyguard is going
      *         away, false otherwise
      */
-    public static boolean isKeyguardGoingAwayTransit(int transit) {
+    public static boolean isKeyguardGoingAwayTransitOld(int transit) {
         return transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
                 || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
     }
 
-    static boolean isKeyguardTransit(@TransitionOldType int transit) {
-        return isKeyguardGoingAwayTransit(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+    static boolean isKeyguardTransitOld(@TransitionOldType int transit) {
+        return isKeyguardGoingAwayTransitOld(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
                 || transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
     }
 
-    static boolean isTaskTransit(@TransitionOldType int transit) {
-        return isTaskOpenTransit(transit)
+    static boolean isTaskTransitOld(@TransitionOldType int transit) {
+        return isTaskOpenTransitOld(transit)
                 || transit == TRANSIT_OLD_TASK_CLOSE
                 || transit == TRANSIT_OLD_TASK_TO_BACK;
     }
 
-    private static  boolean isTaskOpenTransit(@TransitionOldType int transit) {
+    private static  boolean isTaskOpenTransitOld(@TransitionOldType int transit) {
         return transit == TRANSIT_OLD_TASK_OPEN
                 || transit == TRANSIT_OLD_TASK_OPEN_BEHIND
                 || transit == TRANSIT_OLD_TASK_TO_FRONT;
     }
 
-    static boolean isActivityTransit(@TransitionOldType int transit) {
+    static boolean isActivityTransitOld(@TransitionOldType int transit) {
         return transit == TRANSIT_OLD_ACTIVITY_OPEN
                 || transit == TRANSIT_OLD_ACTIVITY_CLOSE
                 || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH;
     }
 
-    static boolean isChangeTransit(@TransitionOldType int transit) {
+    static boolean isChangeTransitOld(@TransitionOldType int transit) {
         return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
     }
 
-    static boolean isClosingTransit(@TransitionOldType int transit) {
+    static boolean isClosingTransitOld(@TransitionOldType int transit) {
         return transit == TRANSIT_OLD_ACTIVITY_CLOSE
                 || transit == TRANSIT_OLD_TASK_CLOSE
                 || transit == TRANSIT_OLD_WALLPAPER_CLOSE
@@ -2390,6 +2321,51 @@
                 || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
     }
 
+    static boolean isNormalTransit(@TransitionType int transit) {
+        return transit == TRANSIT_OPEN
+                || transit == TRANSIT_CLOSE
+                || transit == TRANSIT_TO_FRONT
+                || transit == TRANSIT_TO_BACK;
+    }
+
+    static boolean isKeyguardTransit(@TransitionType int transit) {
+        return transit == TRANSIT_KEYGUARD_GOING_AWAY
+                || transit == TRANSIT_KEYGUARD_OCCLUDE
+                || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
+    }
+
+    @TransitionType int getKeyguardTransition() {
+        // In case we unocclude Keyguard and occlude it again, meaning that we never actually
+        // unoccclude/occlude Keyguard, but just run a normal transition.
+        final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
+        if (occludeIndex != -1
+                && occludeIndex < mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE)) {
+            return TRANSIT_NONE;
+        }
+
+        for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
+            final @TransitionType int transit = mNextAppTransitionRequests.get(i);
+            if (isKeyguardTransit(transit)) {
+                return transit;
+            }
+        }
+        return TRANSIT_NONE;
+    }
+
+    @TransitionType int getFirstAppTransition() {
+        for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
+            final @TransitionType int transit = mNextAppTransitionRequests.get(i);
+            if (transit != TRANSIT_NONE && !isKeyguardTransit(transit)) {
+                return transit;
+            }
+        }
+        return TRANSIT_NONE;
+    }
+
+    boolean containsTransitRequest(@TransitionType int transit) {
+        return mNextAppTransitionRequests.contains(transit);
+    }
+
     /**
      * @return whether the transition should show the thumbnail being scaled down.
      */
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 069f962..64cbb0de 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -17,19 +17,31 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
@@ -38,6 +50,10 @@
 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -46,7 +62,7 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
+import static com.android.server.wm.AppTransition.isNormalTransit;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -54,6 +70,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -63,7 +80,9 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionFlags;
 import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -97,6 +116,30 @@
     }
 
     /**
+     * Returns the currently visible window that is associated with the wallpaper in case we are
+     * transitioning from an activity with a wallpaper to one without.
+     */
+    private @Nullable WindowState getOldWallpaper() {
+        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
+        final @TransitionType int firstTransit =
+                mDisplayContent.mAppTransition.getFirstAppTransition();
+
+        final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
+                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */);
+        final boolean showWallpaper = wallpaperTarget != null
+                && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+                // Update task open transition to wallpaper transition when wallpaper is visible.
+                // (i.e.launching app info activity from recent tasks)
+                || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT)
+                && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null)
+                && mWallpaperControllerLocked.isWallpaperVisible()));
+        // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
+        // don't consider upgrading to wallpaper transition.
+        return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
+                ? null : wallpaperTarget;
+    }
+
+    /**
      * Handle application transition for given display.
      */
     void handleAppTransitionReady() {
@@ -109,13 +152,8 @@
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
-        // TODO(new-app-transition): Compute the best transition from
-        //  appTransition.mNextAppTransitionRequests
+        // TODO(new-app-transition): Remove code using appTransition.getAppTransition()
         final AppTransition appTransition = mDisplayContent.mAppTransition;
-        int transit = appTransition.getAppTransitionOld();
-        if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
-            transit = WindowManager.TRANSIT_OLD_UNSET;
-        }
         mDisplayContent.mSkipAppTransitionAnimation = false;
         mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
 
@@ -147,17 +185,18 @@
         mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                 mDisplayContent.mOpeningApps);
 
-        // Determine if closing and opening app token sets are wallpaper targets, in which case
-        // special animations are needed.
-        final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
-        final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
-                && hasWallpaperTarget;
-        final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
-                && hasWallpaperTarget;
+        final @TransitionOldType int transit = getTransitCompatType(
+                mDisplayContent.mAppTransition,
+                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper());
 
-        transit = maybeUpdateTransitToTranslucentAnim(transit);
-        transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
-                closingAppHasWallpaper);
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "handleAppTransitionReady: displayId=%d appTransition={%s}"
+                + " openingApps=[%s] closingApps=[%s] transit=%s",
+                mDisplayContent.mDisplayId,
+                appTransition.toString(),
+                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                AppTransition.appTransitionOldToString(transit));
 
         // Find the layout params of the top-most application window in the tokens, which is
         // what will control the animation theme. If all closing windows are obscured, then there is
@@ -191,8 +230,7 @@
                     topClosingApp, topChangingApp);
 
             final int flags = appTransition.getTransitFlags();
-            layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
-                    mDisplayContent.mOpeningApps);
+            layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
             handleNonAppWindowsInTransition(transit, flags);
             appTransition.postAnimationCallback();
             appTransition.clear();
@@ -222,6 +260,162 @@
                 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
     }
 
+    /**
+     * Get old transit type based on the current transit requests.
+     *
+     * @param appTransition {@link AppTransition} for managing app transition state.
+     * @param openingApps {@link ActivityRecord}s which are becoming visible.
+     * @param closingApps {@link ActivityRecord}s which are becoming invisible.
+     * @param wallpaperTarget If non-null, this is the currently visible window that is associated
+     *                        with the wallpaper.
+     * @param oldWallpaper The currently visible window that is associated with the wallpaper in
+     *                     case we are transitioning from an activity with a wallpaper to one
+     *                     without. Otherwise null.
+     */
+    static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
+            ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
+            @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper) {
+
+        // Determine if closing and opening app token sets are wallpaper targets, in which case
+        // special animations are needed.
+        final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
+                && wallpaperTarget != null;
+        final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
+                && wallpaperTarget != null;
+
+        // Keyguard transit has highest priority.
+        switch (appTransition.getKeyguardTransition()) {
+            case TRANSIT_KEYGUARD_GOING_AWAY:
+                return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+                        : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+            case TRANSIT_KEYGUARD_OCCLUDE:
+                // When there is a closing app, the keyguard has already been occluded by an
+                // activity, and another activity has started on top of that activity, so normal
+                // app transition animation should be used.
+                return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
+                        : TRANSIT_OLD_ACTIVITY_OPEN;
+            case TRANSIT_KEYGUARD_UNOCCLUDE:
+                return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+        }
+
+        final @TransitionFlags int flags = appTransition.getTransitFlags();
+        final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
+
+        // Special transitions
+        // TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
+        if (appTransition.containsTransitRequest(TRANSIT_CHANGE_WINDOWING_MODE)) {
+            return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+        }
+        if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
+            return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+        }
+        if (firstTransit == TRANSIT_NONE) {
+            return TRANSIT_OLD_NONE;
+        }
+
+        /*
+         * There are cases where we open/close a new task/activity, but in reality only a
+         * translucent activity on top of existing activities is opening/closing. For that one, we
+         * have a different animation because non of the task/activity animations actually work well
+         * with translucent apps.
+         */
+        if (isNormalTransit(firstTransit)) {
+            boolean allOpeningVisible = true;
+            boolean allTranslucentOpeningApps = !openingApps.isEmpty();
+            for (int i = openingApps.size() - 1; i >= 0; i--) {
+                final ActivityRecord activity = openingApps.valueAt(i);
+                if (!activity.isVisible()) {
+                    allOpeningVisible = false;
+                    if (activity.fillsParent()) {
+                        allTranslucentOpeningApps = false;
+                    }
+                }
+            }
+            boolean allTranslucentClosingApps = !closingApps.isEmpty();
+            for (int i = closingApps.size() - 1; i >= 0; i--) {
+                if (closingApps.valueAt(i).fillsParent()) {
+                    allTranslucentClosingApps = false;
+                    break;
+                }
+            }
+
+            if (allTranslucentClosingApps && allOpeningVisible) {
+                return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
+            }
+            if (allTranslucentOpeningApps && closingApps.isEmpty()) {
+                return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+            }
+        }
+
+        final ActivityRecord topOpeningApp = getTopApp(openingApps,
+                false /* ignoreHidden */);
+        final ActivityRecord topClosingApp = getTopApp(closingApps,
+                true /* ignoreHidden */);
+
+        if (closingAppHasWallpaper && openingAppHasWallpaper) {
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
+            switch (firstTransit) {
+                case TRANSIT_OPEN:
+                    return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+                case TRANSIT_CLOSE:
+                    return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+            }
+        } else if (oldWallpaper != null && !openingApps.isEmpty()
+                && !openingApps.contains(oldWallpaper.mActivityRecord)
+                && closingApps.contains(oldWallpaper.mActivityRecord)
+                && topClosingApp == oldWallpaper.mActivityRecord) {
+            // We are transitioning from an activity with a wallpaper to one without.
+            return TRANSIT_OLD_WALLPAPER_CLOSE;
+        } else if (wallpaperTarget != null && wallpaperTarget.isVisible()
+                && openingApps.contains(wallpaperTarget.mActivityRecord)
+                && topOpeningApp == wallpaperTarget.mActivityRecord
+                /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) {
+            // We are transitioning from an activity without
+            // a wallpaper to now showing the wallpaper
+            return TRANSIT_OLD_WALLPAPER_OPEN;
+        }
+
+        final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
+                openingApps, closingApps, true /* visible */);
+        final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
+                openingApps, closingApps, false /* visible */);
+        final boolean isActivityOpening = !openingWcs.isEmpty()
+                && openingWcs.valueAt(0).asActivityRecord() != null;
+        final boolean isActivityClosing = !closingWcs.isEmpty()
+                && closingWcs.valueAt(0).asActivityRecord() != null;
+        final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening;
+        final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing;
+
+        if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
+            return TRANSIT_OLD_TASK_TO_FRONT;
+        }
+        if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
+            return TRANSIT_OLD_TASK_TO_BACK;
+        }
+        if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
+            if (isTaskOpening) {
+                return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
+                        ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
+            }
+            if (isActivityOpening) {
+                return TRANSIT_OLD_ACTIVITY_OPEN;
+            }
+        }
+        if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
+            if (isTaskClosing) {
+                return TRANSIT_OLD_TASK_CLOSE;
+            }
+            if (isActivityClosing) {
+                return TRANSIT_OLD_ACTIVITY_CLOSE;
+            }
+        }
+        if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH)
+                && !openingWcs.isEmpty() && !openingApps.isEmpty()) {
+            return TRANSIT_OLD_ACTIVITY_RELAUNCH;
+        }
+        return TRANSIT_OLD_NONE;
+    }
+
     private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
         final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
         return mainWindow != null ? mainWindow.mAttrs : null;
@@ -691,137 +885,6 @@
         return true;
     }
 
-    private int maybeUpdateTransitToWallpaper(@TransitionOldType int transit,
-            boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
-        // Given no app transition pass it through instead of a wallpaper transition.
-        // Never convert the crashing transition.
-        // Never convert a change transition since the top activity isn't changing and will likely
-        // still be above an opening wallpaper.
-        if (transit == TRANSIT_OLD_NONE || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE
-                || AppTransition.isChangeTransit(transit)) {
-            return transit;
-        }
-
-        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
-        final boolean showWallpaper = wallpaperTarget != null
-                && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
-                // Update task open transition to wallpaper transition when wallpaper is visible.
-                // (i.e.launching app info activity from recent tasks)
-                || ((transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT)
-                && mWallpaperControllerLocked.isWallpaperVisible()));
-        // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
-        // don't consider upgrading to wallpaper transition.
-        final WindowState oldWallpaper =
-                (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
-                        ? null
-                        : wallpaperTarget;
-        final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
-        final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
-        final ActivityRecord topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
-                false /* ignoreHidden */);
-        final ActivityRecord topClosingApp = getTopApp(mDisplayContent.mClosingApps,
-                true /* ignoreHidden */);
-
-        boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
-        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
-                        wallpaperTarget, oldWallpaper, openingApps, closingApps);
-
-        if (openingCanBeWallpaperTarget && transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) {
-            transit = TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "New transit: %s", AppTransition.appTransitionOldToString(transit));
-        }
-        // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
-        // relies on the fact that we always execute a Keyguard transition after preparing one.
-        else if (!isKeyguardGoingAwayTransit(transit)) {
-            if (closingAppHasWallpaper && openingAppHasWallpaper) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
-                switch (transit) {
-                    case TRANSIT_OLD_ACTIVITY_OPEN:
-                    case TRANSIT_OLD_TASK_OPEN:
-                    case TRANSIT_OLD_TASK_TO_FRONT:
-                        transit = TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
-                        break;
-                    case TRANSIT_OLD_ACTIVITY_CLOSE:
-                    case TRANSIT_OLD_TASK_CLOSE:
-                    case TRANSIT_OLD_TASK_TO_BACK:
-                        transit = TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
-                        break;
-                }
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "New transit: %s", AppTransition.appTransitionOldToString(transit));
-            } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
-                    && !openingApps.contains(oldWallpaper.mActivityRecord)
-                    && closingApps.contains(oldWallpaper.mActivityRecord)
-                    && topClosingApp == oldWallpaper.mActivityRecord) {
-                // We are transitioning from an activity with a wallpaper to one without.
-                transit = TRANSIT_OLD_WALLPAPER_CLOSE;
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "New transit away from wallpaper: %s",
-                                AppTransition.appTransitionOldToString(transit));
-            } else if (wallpaperTarget != null && wallpaperTarget.isVisible()
-                    && openingApps.contains(wallpaperTarget.mActivityRecord)
-                    && topOpeningApp == wallpaperTarget.mActivityRecord
-                    && transit != TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE) {
-                // We are transitioning from an activity without
-                // a wallpaper to now showing the wallpaper
-                transit = TRANSIT_OLD_WALLPAPER_OPEN;
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "New transit into wallpaper: %s",
-                        AppTransition.appTransitionOldToString(transit));
-            }
-        }
-        return transit;
-    }
-
-    /**
-     * There are cases where we open/close a new task/activity, but in reality only a translucent
-     * activity on top of existing activities is opening/closing. For that one, we have a different
-     * animation because non of the task/activity animations actually work well with translucent
-     * apps.
-     *
-     * @param transit The current transition type.
-     * @return The current transition type or
-     *         {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE}/
-     *         {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
-     *         situation.
-     */
-    @VisibleForTesting
-    int maybeUpdateTransitToTranslucentAnim(@TransitionOldType int transit) {
-        if (AppTransition.isChangeTransit(transit)) {
-            // There's no special animation to handle change animations with translucent apps
-            return transit;
-        }
-        final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
-                || AppTransition.isActivityTransit(transit);
-        boolean allOpeningVisible = true;
-        boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
-        for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mDisplayContent.mOpeningApps.valueAt(i);
-            if (!activity.isVisible()) {
-                allOpeningVisible = false;
-                if (activity.fillsParent()) {
-                    allTranslucentOpeningApps = false;
-                }
-            }
-        }
-        boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty();
-        for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
-            if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) {
-                allTranslucentClosingApps = false;
-                break;
-            }
-        }
-
-        if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
-            return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
-        }
-        if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
-            return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
-        }
-        return transit;
-    }
-
     /**
      * Identifies whether the current transition occurs within a single task or not. This is used
      * to determine whether animations should be clipped to the task bounds instead of stack bounds.
@@ -855,7 +918,7 @@
         return true;
     }
 
-    private boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
+    private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
         for (int i = apps.size() - 1; i >= 0; i--) {
             if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
                 return true;
@@ -873,7 +936,7 @@
      *                        {@link ActivityRecord#isVisible}.
      * @return The top {@link ActivityRecord}.
      */
-    private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
+    private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
             boolean ignoreInvisible) {
         int topPrefixOrderIndex = Integer.MIN_VALUE;
         ActivityRecord topApp = null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 388422e..4133ea2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -73,9 +73,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
@@ -627,12 +626,6 @@
      */
     private boolean mInEnsureActivitiesVisible = false;
 
-    /**
-     * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to
-     * prevent duplicate requests to input.
-     */
-    WindowState mLastRequestedFocus = null;
-
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -4547,22 +4540,6 @@
         }
     }
 
-    void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
-            boolean alwaysKeepCurrent) {
-        prepareAppTransitionOld(transit, alwaysKeepCurrent, 0 /* flags */,
-                false /* forceOverride */);
-    }
-
-    void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
-            boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
-            boolean forceOverride) {
-        final boolean prepared = mAppTransition.prepareAppTransitionOld(
-                transit, alwaysKeepCurrent, flags, forceOverride);
-        if (prepared && okToAnimate()) {
-            mSkipAppTransitionAnimation = false;
-        }
-    }
-
     void prepareAppTransition(@WindowManager.TransitionType int transit) {
         prepareAppTransition(transit, 0 /* flags */);
     }
@@ -4641,10 +4618,8 @@
 
     /** Check if pending app transition is for activity / task launch. */
     boolean isNextTransitionForward() {
-        final int transit = mAppTransition.getAppTransitionOld();
-        return transit == TRANSIT_OLD_ACTIVITY_OPEN
-                || transit == TRANSIT_OLD_TASK_OPEN
-                || transit == TRANSIT_OLD_TASK_TO_FRONT;
+        return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
+                || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
     }
 
     /**
@@ -5403,20 +5378,6 @@
         forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
     }
 
-    /**
-     * Similar to {@link RootWindowContainer#isAnyNonToastWindowVisibleForUid(int)}, but
-     * used for pid.
-     */
-    boolean isAnyNonToastWindowVisibleForPid(int pid) {
-        final PooledPredicate p = PooledLambda.obtainPredicate(
-                WindowState::isNonToastWindowVisibleForPid,
-                PooledLambda.__(WindowState.class), pid);
-
-        final WindowState w = getWindow(p);
-        p.recycle();
-        return w != null;
-    }
-
     Context getDisplayUiContext() {
         return mDisplayPolicy.getSystemUiContext();
     }
@@ -5607,7 +5568,7 @@
         }
 
         @Override
-        public void onAppTransitionCancelledLocked(int transit) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
             continueUpdateOrientationForDiffOrienLaunchingApp();
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 11a436e..f14a2ee 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -118,7 +118,6 @@
 import android.app.ActivityThread;
 import android.app.LoadedApk;
 import android.app.ResourcesManager;
-import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -602,7 +601,7 @@
             }
 
             @Override
-            public int onAppTransitionStartingLocked(int transit, long duration,
+            public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
                     long statusBarAnimationStartTime, long statusBarAnimationDuration) {
                 mHandler.post(() -> {
                     StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -615,7 +614,7 @@
             }
 
             @Override
-            public void onAppTransitionCancelledLocked(int transit) {
+            public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
                 mHandler.post(mAppTransitionCancelled);
             }
 
@@ -2746,7 +2745,7 @@
         final int dockedAppearance = updateLightStatusBarLw(0 /* appearance */,
                 mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState,
                 mDockedStackBounds);
-        final int disableFlags = win.getSystemUiVisibility() & StatusBarManager.DISABLE_MASK;
+        final int disableFlags = win.getDisableFlags();
         final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
         final WindowState navColorWin = chooseNavigationColorWindowLw(
                 mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e7d8ad6..ad4e64a 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -45,7 +45,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DragEvent;
@@ -64,6 +63,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 
 import java.util.ArrayList;
 
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
new file mode 100644
index 0000000..4793e1b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
@@ -0,0 +1,361 @@
+/*
+ * 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.wm;
+
+import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.attestation.IImpressionAttestationService;
+import android.service.attestation.ImpressionAttestationService;
+import android.service.attestation.ImpressionToken;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+
+/**
+ * Handles requests into {@link ImpressionAttestationService}
+ *
+ * Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
+ * blocking calls into another service.
+ */
+public class ImpressionAttestationController {
+    private static final String TAG = "ImpressionAttestationController";
+    private static final boolean DEBUG = false;
+
+    private final Object mServiceConnectionLock = new Object();
+
+    @GuardedBy("mServiceConnectionLock")
+    private ImpressionAttestationServiceConnection mServiceConnection;
+
+    private final Context mContext;
+
+    /**
+     * Lock used for the cached {@link #mImpressionAlgorithms} array
+     */
+    private final Object mImpressionAlgorithmsLock = new Object();
+
+    @GuardedBy("mImpressionAlgorithmsLock")
+    private String[] mImpressionAlgorithms;
+
+    private final Handler mHandler;
+
+    private final String mSalt;
+
+    private interface Command {
+        void run(IImpressionAttestationService service) throws RemoteException;
+    }
+
+    ImpressionAttestationController(Context context) {
+        mContext = context;
+        mHandler = new Handler(Looper.getMainLooper());
+        mSalt = UUID.randomUUID().toString();
+    }
+
+    String[] getSupportedImpressionAlgorithms() {
+        // We have a separate lock for the impression algorithm array since it doesn't need to make
+        // the request through the service connection. Instead, we have a lock to ensure we can
+        // properly cache the impression algorithms array so we don't need to call into the
+        // ExtServices process for each request.
+        synchronized (mImpressionAlgorithmsLock) {
+            // Already have cached values
+            if (mImpressionAlgorithms != null) {
+                return mImpressionAlgorithms;
+            }
+
+            final ServiceInfo serviceInfo = getServiceInfo();
+            if (serviceInfo == null) return null;
+
+            final PackageManager pm = mContext.getPackageManager();
+            final Resources res;
+            try {
+                res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.e(TAG, "Error getting application resources for " + serviceInfo, e);
+                return null;
+            }
+
+            final int resourceId = serviceInfo.metaData.getInt(
+                    SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
+            mImpressionAlgorithms = res.getStringArray(resourceId);
+
+            return mImpressionAlgorithms;
+        }
+    }
+
+    boolean verifyImpressionToken(ImpressionToken impressionToken) {
+        final SyncCommand syncCommand = new SyncCommand();
+        Bundle results = syncCommand.run((service, remoteCallback) -> {
+            try {
+                service.verifyImpressionToken(mSalt, impressionToken, remoteCallback);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+            }
+        });
+
+        return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+    }
+
+    ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+            String hashAlgorithm) {
+        final SyncCommand syncCommand = new SyncCommand();
+        Bundle results = syncCommand.run((service, remoteCallback) -> {
+            try {
+                service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm,
+                        remoteCallback);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+            }
+        });
+
+        return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+    }
+
+    /**
+     * Run a command, starting the service connection if necessary.
+     */
+    private void connectAndRun(@NonNull Command command) {
+        synchronized (mServiceConnectionLock) {
+            mHandler.resetTimeoutMessage();
+            if (mServiceConnection == null) {
+                if (DEBUG) Slog.v(TAG, "creating connection");
+
+                // Create the connection
+                mServiceConnection = new ImpressionAttestationServiceConnection();
+
+                final ComponentName component = getServiceComponentName();
+                if (DEBUG) Slog.v(TAG, "binding to: " + component);
+                if (component != null) {
+                    final Intent intent = new Intent();
+                    intent.setComponent(component);
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mContext.bindServiceAsUser(intent, mServiceConnection,
+                                Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
+                        if (DEBUG) Slog.v(TAG, "bound");
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            }
+
+            mServiceConnection.runCommandLocked(command);
+        }
+    }
+
+    @Nullable
+    private ServiceInfo getServiceInfo() {
+        final String packageName =
+                mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
+        if (packageName == null) {
+            Slog.w(TAG, "no external services package!");
+            return null;
+        }
+
+        final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            Slog.w(TAG, "No valid components found.");
+            return null;
+        }
+        return resolveInfo.serviceInfo;
+    }
+
+    @Nullable
+    private ComponentName getServiceComponentName() {
+        final ServiceInfo serviceInfo = getServiceInfo();
+        if (serviceInfo == null) return null;
+
+        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+                .equals(serviceInfo.permission)) {
+            Slog.w(TAG, name.flattenToShortString() + " requires permission "
+                    + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+            return null;
+        }
+
+        if (DEBUG) Slog.v(TAG, "getServiceComponentName(): " + name);
+        return name;
+    }
+
+    private class SyncCommand {
+        private static final int WAIT_TIME_S = 5;
+        private Bundle mResult;
+        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+        public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+            connectAndRun(service -> {
+                RemoteCallback callback = new RemoteCallback(result -> {
+                    mResult = result;
+                    mCountDownLatch.countDown();
+                });
+                func.accept(service, callback);
+            });
+
+            try {
+                mCountDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                Slog.e(TAG, "Failed to wait for command", e);
+            }
+
+            return mResult;
+        }
+    }
+
+    private class ImpressionAttestationServiceConnection implements ServiceConnection {
+        @GuardedBy("mServiceConnectionLock")
+        private IImpressionAttestationService mRemoteService;
+
+        @GuardedBy("mServiceConnectionLock")
+        private ArrayList<Command> mQueuedCommands;
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
+            synchronized (mServiceConnectionLock) {
+                mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+                if (mQueuedCommands != null) {
+                    final int size = mQueuedCommands.size();
+                    if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
+                    for (int i = 0; i < size; i++) {
+                        final Command queuedCommand = mQueuedCommands.get(i);
+                        try {
+                            if (DEBUG) Slog.v(TAG, "running queued command #" + i);
+                            queuedCommand.run(mRemoteService);
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "exception calling " + name + ": " + e);
+                        }
+                    }
+                    mQueuedCommands = null;
+                } else if (DEBUG) {
+                    Slog.d(TAG, "no queued commands");
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, "onServiceDisconnected(): " + name);
+            synchronized (mServiceConnectionLock) {
+                mRemoteService = null;
+            }
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, "onBindingDied(): " + name);
+            synchronized (mServiceConnectionLock) {
+                mRemoteService = null;
+            }
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, "onNullBinding(): " + name);
+            synchronized (mServiceConnectionLock) {
+                mRemoteService = null;
+            }
+        }
+
+        /**
+         * Only call while holding {@link #mServiceConnectionLock}
+         */
+        private void runCommandLocked(Command command) {
+            if (mRemoteService == null) {
+                if (DEBUG) Slog.d(TAG, "service is null; queuing command");
+                if (mQueuedCommands == null) {
+                    mQueuedCommands = new ArrayList<>(1);
+                }
+                mQueuedCommands.add(command);
+            } else {
+                try {
+                    if (DEBUG) Slog.v(TAG, "running command right away");
+                    command.run(mRemoteService);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "exception calling service: " + e);
+                }
+            }
+        }
+    }
+
+    private class Handler extends android.os.Handler {
+        static final long SERVICE_SHUTDOWN_TIMEOUT_MILLIS = 10000; // 10s
+        static final int MSG_SERVICE_SHUTDOWN_TIMEOUT = 1;
+
+        Handler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_SERVICE_SHUTDOWN_TIMEOUT) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Shutting down service");
+                }
+                synchronized (mServiceConnectionLock) {
+                    if (mServiceConnection != null) {
+                        mContext.unbindService(mServiceConnection);
+                        mServiceConnection = null;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Set a timer for {@link #SERVICE_SHUTDOWN_TIMEOUT_MILLIS} so we can tear down the service
+         * if it's inactive. The requests will be coming from apps so it's hard to tell how often
+         * the requests can come in. Therefore, we leave the service running if requests continue
+         * to come in. Once there's been no activity for 10s, we can shut down the service and
+         * restart when we get a new request.
+         */
+        void resetTimeoutMessage() {
+            if (DEBUG) {
+                Slog.v(TAG, "Reset shutdown message");
+            }
+            removeMessages(MSG_SERVICE_SHUTDOWN_TIMEOUT);
+            sendEmptyMessageDelayed(MSG_SERVICE_SHUTDOWN_TIMEOUT, SERVICE_SHUTDOWN_TIMEOUT_MILLIS);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 457df4e..efd9e2a 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -73,8 +73,8 @@
 final class InputMonitor {
     private final WindowManagerService mService;
 
-    // Current window with input focus for keys and other non-touch events.  May be null.
-    private WindowState mInputFocus;
+    // Current input focus token for keys and other non-touch events.  May be null.
+    private IBinder mInputFocus = null;
 
     // When true, need to call updateInputWindowsLw().
     private boolean mUpdateInputWindowsNeeded = true;
@@ -377,31 +377,62 @@
     }
 
     /**
-     * Called when the current input focus changes.
+     * Called when the current input focus changes. Will apply it in next updateInputWindows.
      * Layer assignment is assumed to be complete by the time this is called.
      */
-    public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
+    void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
         ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
                 newWindow, mDisplayId);
+        final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null;
+        if (focus == mInputFocus) {
+            return;
+        }
 
-        if (newWindow != mInputFocus) {
-            if (newWindow != null && newWindow.canReceiveKeys()) {
-                // Displaying a window implicitly causes dispatching to be unpaused.
-                // This is to protect against bugs if someone pauses dispatching but
-                // forgets to resume.
-                newWindow.mToken.paused = false;
-            }
+        if (newWindow != null && newWindow.canReceiveKeys()) {
+            // Displaying a window implicitly causes dispatching to be unpaused.
+            // This is to protect against bugs if someone pauses dispatching but
+            // forgets to resume.
+            newWindow.mToken.paused = false;
+        }
 
-            mInputFocus = newWindow;
-            setUpdateInputWindowsNeededLw();
+        setUpdateInputWindowsNeededLw();
 
-            if (updateInputWindows) {
-                updateInputWindowsLw(false /*force*/);
-            }
+        if (updateInputWindows) {
+            updateInputWindowsLw(false /*force*/);
         }
     }
 
-    public void setFocusedAppLw(ActivityRecord newApp) {
+    /**
+     * Called when the current input focus changes.
+     */
+    private void updateInputFocusRequest() {
+        final WindowState focus = mDisplayContent.mCurrentFocus;
+        final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
+
+        if (focusToken == null) {
+            mInputFocus = null;
+            return;
+        }
+
+        if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) {
+            Slog.v(TAG_WM, "Focus not requested for window=%" + focus
+                    + " because it has no surface or is not focusable.");
+            mInputFocus = null;
+            return;
+        }
+
+        if (focusToken == mInputFocus) {
+            return;
+        }
+
+        mInputFocus = focusToken;
+        mInputTransaction.setFocusedWindow(mInputFocus, mDisplayId);
+        EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus,
+                "reason=UpdateInputWindows");
+        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+    }
+
+    void setFocusedAppLw(ActivityRecord newApp) {
         // Focused app has changed.
         mService.mInputManager.setFocusedApplication(mDisplayId,
                 newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
@@ -482,30 +513,6 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        private void updateInputFocusRequest() {
-            if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) {
-                return;
-            }
-
-            final WindowState focus = mDisplayContent.mCurrentFocus;
-            if (focus == null || focus.mInputChannelToken == null) {
-                mDisplayContent.mLastRequestedFocus = focus;
-                return;
-            }
-
-            if (!focus.mWinAnimator.hasSurface()) {
-                Slog.v(TAG_WM, "Focus not requested for window=%" + focus
-                        + " because it has no surface.");
-                return;
-            }
-
-            mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId);
-            EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
-                    "Focus request " + focus, "reason=UpdateInputWindows");
-            mDisplayContent.mLastRequestedFocus = focus;
-            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
-        }
-
         @Override
         public void accept(WindowState w) {
             final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 9339f34..7a4d13c 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -63,6 +63,10 @@
         return mHandle.displayId;
     }
 
+    boolean isFocusable() {
+        return mHandle.focusable;
+    }
+
     InputApplicationHandle getInputApplicationHandle() {
         return mHandle.inputApplicationHandle;
     }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 7b60092..c3b6149 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -26,18 +26,14 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
 
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
@@ -211,11 +207,7 @@
                     1 /* keyguardGoingAway */,
                     "keyguardGoingAway");
             mRootWindowContainer.getDefaultDisplay()
-                    .prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
-                            false /* alwaysKeepCurrent */, convertTransitFlags(flags),
-                            false /* forceOverride */);
-            mRootWindowContainer.getDefaultDisplay()
-                    .requestTransitionAndLegacyPrepare(TRANSIT_KEYGUARD_GOING_AWAY,
+                    .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                             convertTransitFlags(flags));
             updateKeyguardSleepToken();
 
@@ -363,10 +355,6 @@
             mService.deferWindowLayout();
             try {
                 mRootWindowContainer.getDefaultDisplay()
-                        .prepareAppTransitionOld(resolveOccludeTransit(),
-                                false /* alwaysKeepCurrent */, 0 /* flags */,
-                                true /* forceOverride */);
-                mRootWindowContainer.getDefaultDisplay()
                         .prepareAppTransition(
                                 isDisplayOccluded(DEFAULT_DISPLAY)
                                         ? TRANSIT_KEYGUARD_OCCLUDE
@@ -397,10 +385,7 @@
         // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
         final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
         if (mKeyguardShowing && canDismissKeyguard()
-                && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
-            dc.prepareAppTransitionOld(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
-                    0 /* flags */, true /* forceOverride */);
-            dc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE);
+                && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
             mWindowManager.executeAppTransition();
         }
     }
@@ -432,28 +417,6 @@
                 || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
     }
 
-    private int resolveOccludeTransit() {
-        // TODO(new-app-transition): Remove after migrating to the enw transit system.
-        final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
-        if (mBeforeUnoccludeTransit != TRANSIT_OLD_UNSET
-                && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE
-                // TODO(b/113840485): Handle app transition for individual display.
-                && isDisplayOccluded(DEFAULT_DISPLAY)) {
-
-            // Reuse old transit in case we are occluding Keyguard again, meaning that we never
-            // actually occclude/unocclude Keyguard, but just run a normal transition.
-            return mBeforeUnoccludeTransit;
-            // TODO(b/113840485): Handle app transition for individual display.
-        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
-
-            // Save transit in case we dismiss/occlude Keyguard shortly after.
-            mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransitionOld();
-            return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
-        } else {
-            return TRANSIT_OLD_KEYGUARD_OCCLUDE;
-        }
-    }
-
     private void dismissMultiWindowModeForTaskIfNeeded(
             @Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) {
         // If turningScreenOn is true, it means that the visibility state has changed from
diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
index 2507851..c8558dd 100644
--- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
+++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -26,10 +26,10 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.utils.UserTokenWatcher;
 import com.android.server.wm.LockTaskController.LockTaskToken;
diff --git a/services/core/java/com/android/server/wm/MirrorActiveUids.java b/services/core/java/com/android/server/wm/MirrorActiveUids.java
index 0047942..4e7f1d4 100644
--- a/services/core/java/com/android/server/wm/MirrorActiveUids.java
+++ b/services/core/java/com/android/server/wm/MirrorActiveUids.java
@@ -18,7 +18,10 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 
-import android.util.SparseIntArray;
+import android.app.ActivityManager.ProcessState;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
 
 /**
  * This is a partial mirror of {@link @com.android.server.am.ActiveUids}. It is already thread
@@ -26,28 +29,64 @@
  * adjustment) or getting state from window manager (background start check).
  */
 class MirrorActiveUids {
-    private SparseIntArray mUidStates = new SparseIntArray();
+    private final SparseArray<UidRecord> mUidStates = new SparseArray<>();
 
     synchronized void onUidActive(int uid, int procState) {
-        mUidStates.put(uid, procState);
+        UidRecord r = mUidStates.get(uid);
+        if (r == null) {
+            r = new UidRecord();
+            mUidStates.put(uid, r);
+        }
+        r.mProcState = procState;
     }
 
     synchronized void onUidInactive(int uid) {
         mUidStates.delete(uid);
     }
 
-    synchronized void onActiveUidsCleared() {
-        mUidStates.clear();
-    }
-
     synchronized void onUidProcStateChanged(int uid, int procState) {
-        final int index = mUidStates.indexOfKey(uid);
-        if (index >= 0) {
-            mUidStates.setValueAt(index, procState);
+        final UidRecord r = mUidStates.get(uid);
+        if (r != null) {
+            r.mProcState = procState;
         }
     }
 
-    synchronized int getUidState(int uid) {
-        return mUidStates.get(uid, PROCESS_STATE_NONEXISTENT);
+    synchronized @ProcessState int getUidState(int uid) {
+        final UidRecord r = mUidStates.get(uid);
+        return r != null ? r.mProcState : PROCESS_STATE_NONEXISTENT;
+    }
+
+    /** Called when the surface of non-application (exclude toast) window is shown or hidden. */
+    synchronized void onNonAppSurfaceVisibilityChanged(int uid, boolean visible) {
+        final UidRecord r = mUidStates.get(uid);
+        if (r != null) {
+            r.mNumNonAppVisibleWindow += visible ? 1 : -1;
+        }
+    }
+
+    /**
+     * Returns {@code true} if the uid has any non-application (exclude toast) window currently
+     * visible to the user. The application window visibility of a uid can be found from
+     * {@link VisibleActivityProcessTracker}.
+     */
+    synchronized boolean hasNonAppVisibleWindow(int uid) {
+        final UidRecord r = mUidStates.get(uid);
+        return r != null && r.mNumNonAppVisibleWindow > 0;
+    }
+
+    synchronized void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix + "NumNonAppVisibleWindowByUid:[");
+        for (int i = mUidStates.size() - 1; i >= 0; i--) {
+            final UidRecord r = mUidStates.valueAt(i);
+            if (r.mNumNonAppVisibleWindow > 0) {
+                pw.print(" " + mUidStates.keyAt(i) + ":" + r.mNumNonAppVisibleWindow);
+            }
+        }
+        pw.println("]");
+    }
+
+    private static final class UidRecord {
+        @ProcessState int mProcState;
+        int mNumNonAppVisibleWindow;
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b117699..823dc51 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -385,12 +385,9 @@
                     mWindowManager.executeAppTransition();
 
                     final Task rootTask = targetStack.getRootTask();
-                    if (rootTask.isOrganized()) {
-                        // Client state may have changed during the recents animation, so force
-                        // send task info so the client can synchronize its state.
-                        mService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                                rootTask, true /* force */);
-                    }
+                    // Client state may have changed during the recents animation, so force
+                    // send task info so the client can synchronize its state.
+                    rootTask.dispatchTaskInfoChangedIfNeeded(true /* force */);
                 } catch (Exception e) {
                     Slog.e(TAG, "Failed to clean up recents activity", e);
                     throw e;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6d29a1d..abee032 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -152,14 +152,14 @@
      */
     final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
         @Override
-        public int onAppTransitionStartingLocked(int transit, long duration,
+        public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
                 long statusBarAnimationStartTime, long statusBarAnimationDuration) {
             continueDeferredCancel();
             return 0;
         }
 
         @Override
-        public void onAppTransitionCancelledLocked(int transit) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
             continueDeferredCancel();
         }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5e80670..7073548 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -39,9 +39,7 @@
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
@@ -53,11 +51,6 @@
 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
-import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -66,6 +59,11 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
@@ -278,7 +276,6 @@
     // Whether tasks have moved and we need to rank the tasks before next OOM scoring
     private boolean mTaskLayersChanged = true;
     private int mTmpTaskLayerRank;
-    private final ArraySet<WindowProcessController> mTmpTaskLayerChangedProcs = new ArraySet<>();
     private final LockedScheduler mRankTaskLayersScheduler;
 
     private boolean mTmpBoolean;
@@ -580,23 +577,6 @@
     }
 
     /**
-     * Returns {@code true} if the callingUid has any non-toast window currently visible to the
-     * user. Also ignores {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_STARTING},
-     * since those windows don't belong to apps.
-     *
-     * @see WindowState#isNonToastOrStarting()
-     */
-    boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
-        final PooledPredicate p = PooledLambda.obtainPredicate(
-                WindowState::isNonToastWindowVisibleForUid,
-                PooledLambda.__(WindowState.class), callingUid);
-
-        final WindowState w = getWindow(p);
-        p.recycle();
-        return w != null;
-    }
-
-    /**
      * Returns the app window token for the input binder if it exist in the system.
      * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
      * AppWindowToken represents an activity which can only exist on one display.
@@ -1018,9 +998,6 @@
             }
         }
 
-        // Remove all deferred displays stacks, tasks, and activities.
-        handleCompleteDeferredRemoval();
-
         forAllDisplays(dc -> {
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             dc.updateSystemGestureExclusion();
@@ -1369,7 +1346,8 @@
         }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             DisplayContent dc = mChildren.get(i);
-            if (dc.isAnyNonToastWindowVisibleForPid(pid)) {
+            if (dc.getWindow(w -> pid == w.mSession.mPid && w.isVisibleNow()
+                    && w.mAttrs.type != WindowManager.LayoutParams.TYPE_TOAST) != null) {
                 outContexts.add(dc.getDisplayUiContext());
             }
         }
@@ -2181,7 +2159,6 @@
 
             // Set a transition to ensure that we don't immediately try and update the visibility
             // of the activity entering PIP
-            r.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
             r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
 
             final boolean singleActivity = task.getChildCount() == 1;
@@ -2213,8 +2190,8 @@
                 // to the list of apps being closed, and request its transition to be ran.
                 final ActivityRecord oldTopActivity = task.getTopMostActivity();
                 if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
-                        && task.getDisplayContent().mAppTransition.getAppTransitionOld()
-                        == TRANSIT_OLD_TASK_TO_BACK) {
+                        && task.getDisplayContent().mAppTransition.containsTransitRequest(
+                                TRANSIT_TO_BACK)) {
                     task.getDisplayContent().mClosingApps.add(oldTopActivity);
                     oldTopActivity.mRequestForceTransition = true;
                 }
@@ -2723,16 +2700,16 @@
             if (task.mLayerRank != oldRank) {
                 task.forAllActivities(activity -> {
                     if (activity.hasProcess()) {
-                        mTmpTaskLayerChangedProcs.add(activity.app);
+                        mTaskSupervisor.onProcessActivityStateChanged(activity.app,
+                                true /* forceBatch */);
                     }
                 });
             }
         }, true /* traverseTopToBottom */);
 
-        for (int i = mTmpTaskLayerChangedProcs.size() - 1; i >= 0; i--) {
-            mTmpTaskLayerChangedProcs.valueAt(i).computeProcessActivityState();
+        if (!mTaskSupervisor.inActivityVisibilityUpdate()) {
+            mTaskSupervisor.computeProcessActivityStateBatch();
         }
-        mTmpTaskLayerChangedProcs.clear();
     }
 
     void clearOtherAppTimeTrackers(AppTimeTracker except) {
@@ -2801,8 +2778,7 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         r.detachFromProcess();
-        r.mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */);
+        r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
                 TRANSIT_FLAG_APP_CRASHED);
         r.destroyIfPossible("handleAppCrashed");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 99e1791..ebf5989 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -62,17 +62,12 @@
 import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -85,12 +80,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
-import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -113,6 +102,12 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -208,6 +203,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.view.WindowManager.TransitionOldType;
 import android.window.ITaskOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
@@ -2045,9 +2041,7 @@
             }
         }
 
-        if (isOrganized()) {
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
-        }
+        dispatchTaskInfoChangedIfNeeded(false /* force */);
     }
 
     private static boolean setTaskDescriptionFromActivityAboveRoot(
@@ -2276,8 +2270,8 @@
         }
         // If the task organizer has changed, then it will already be receiving taskAppeared with
         // the latest task-info thus the task-info won't have changed.
-        if (!taskOrgChanged && isOrganized()) {
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+        if (!taskOrgChanged) {
+            dispatchTaskInfoChangedIfNeeded(false /* force */);
         }
     }
 
@@ -2356,8 +2350,6 @@
      * Initializes a change transition. See {@link SurfaceFreezer} for more information.
      */
     private void initializeChangeTransition(Rect startBounds) {
-        mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
-                false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
         mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE);
         mDisplayContent.mChangingContainers.add(this);
 
@@ -2437,13 +2429,13 @@
 
     @VisibleForTesting
     boolean isInChangeTransition() {
-        return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
+        return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransitOld(mTransit);
     }
 
     @Override
     public SurfaceControl getFreezeSnapshotTarget() {
-        final int transit = mDisplayContent.mAppTransition.getAppTransitionOld();
-        if (!AppTransition.isChangeTransit(transit)) {
+        if (!mDisplayContent.mAppTransition.containsTransitRequest(
+                TRANSIT_CHANGE_WINDOWING_MODE)) {
             return null;
         }
         // Skip creating snapshot if this transition is controlled by a remote animator which
@@ -2452,7 +2444,7 @@
         activityTypes.add(getActivityType());
         final RemoteAnimationAdapter adapter =
                 mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
-                        this, transit, activityTypes);
+                        this, TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, activityTypes);
         if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
             return null;
         }
@@ -3983,7 +3975,7 @@
 
     @Override
     protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
-            int transit, boolean isVoiceInteraction,
+            @TransitionOldType int transit, boolean isVoiceInteraction,
             @Nullable ArrayList<WindowContainer> sources) {
         final RecentsAnimationController control = mWmService.getRecentsAnimationController();
         if (control != null) {
@@ -4106,6 +4098,7 @@
         info.parentTaskId = rootTask == getParent() && rootTask.mCreatedByOrganizer
                 ? rootTask.mTaskId
                 : INVALID_TASK_ID;
+        info.isFocused = isFocused();
     }
 
     @Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -4126,8 +4119,7 @@
                     letterboxActivityBounds)) {
             mLetterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
             // Forcing update to reduce visual jank during the transition.
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                        this, /* force= */ true);
+            dispatchTaskInfoChangedIfNeeded(true /* force */);
         }
     }
 
@@ -5073,12 +5065,11 @@
      */
     void onWindowFocusChanged(boolean hasFocus) {
         updateShadowsRadius(hasFocus, getSyncTransaction());
+        dispatchTaskInfoChangedIfNeeded(false /* force */);
     }
 
     void onPictureInPictureParamsChanged() {
-        if (isOrganized()) {
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, true /* force */);
-        }
+        dispatchTaskInfoChangedIfNeeded(true /* force */);
     }
 
     /**
@@ -6151,12 +6142,8 @@
                         "Prepare close transition: prev=" + prev);
                 if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
                     anim = false;
-                    dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
                     dc.prepareAppTransition(TRANSIT_NONE);
                 } else {
-                    dc.prepareAppTransitionOld(
-                            prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_CLOSE
-                                    : TRANSIT_OLD_TASK_CLOSE, false);
                     dc.prepareAppTransition(TRANSIT_CLOSE);
                 }
                 prev.setVisibility(false);
@@ -6165,24 +6152,18 @@
                         "Prepare open transition: prev=" + prev);
                 if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
                     anim = false;
-                    dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
                     dc.prepareAppTransition(TRANSIT_NONE);
                 } else {
-                    dc.prepareAppTransitionOld(
-                            prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_OPEN
-                                    : next.mLaunchTaskBehind ? TRANSIT_OLD_TASK_OPEN_BEHIND
-                                    : TRANSIT_OLD_TASK_OPEN, /* alwaysKeepCurrent */false);
-                    dc.prepareAppTransition(TRANSIT_OPEN);
+                    dc.prepareAppTransition(TRANSIT_OPEN,
+                            next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
                 }
             }
         } else {
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
             if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
                 anim = false;
-                dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
                 dc.prepareAppTransition(TRANSIT_NONE);
             } else {
-                dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false);
                 dc.prepareAppTransition(TRANSIT_OPEN);
             }
         }
@@ -6442,7 +6423,6 @@
                     "Prepare open transition: starting " + r);
             // TODO(shell-transitions): record NO_ANIMATION flag somewhere.
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, keepCurTransition);
                 dc.prepareAppTransition(TRANSIT_NONE);
                 mTaskSupervisor.mNoAnimActivities.add(r);
             } else {
@@ -6462,7 +6442,6 @@
                         transit = TRANSIT_OLD_TASK_OPEN;
                     }
                 }
-                dc.prepareAppTransitionOld(transit, keepCurTransition);
                 dc.prepareAppTransition(TRANSIT_OPEN);
                 mTaskSupervisor.mNoAnimActivities.remove(r);
             }
@@ -6601,8 +6580,7 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         Task finishedTask = r.getTask();
-        mDisplayContent.prepareAppTransitionOld(
-                TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         r.finishIfPossible(reason, false /* oomAdj */);
 
@@ -6834,9 +6812,8 @@
         forAllActivities(ActivityRecord::removeLaunchTickRunnable);
     }
 
-    private void updateTransitLocked(@WindowManager.TransitionOldType int transit,
-            @WindowManager.TransitionType int transit2, ActivityOptions options,
-            boolean forceOverride) {
+    private void updateTransitLocked(@WindowManager.TransitionType int transit,
+            ActivityOptions options) {
         if (options != null) {
             ActivityRecord r = topRunningActivity();
             if (r != null && !r.isState(RESUMED)) {
@@ -6845,9 +6822,7 @@
                 ActivityOptions.abort(options);
             }
         }
-        mDisplayContent.prepareAppTransitionOld(transit, false,
-                0 /* flags */, forceOverride);
-        mDisplayContent.prepareAppTransition(transit2);
+        mDisplayContent.prepareAppTransition(transit);
     }
 
     final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -6868,8 +6843,7 @@
             if (noAnimation) {
                 ActivityOptions.abort(options);
             } else {
-                updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT, options,
-                        false /* forceOverride */);
+                updateTransitLocked(TRANSIT_TO_FRONT, options);
             }
             return;
         }
@@ -6904,14 +6878,11 @@
 
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
             if (noAnimation) {
-                mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_NONE,
-                        false /* alwaysKeepCurrent */);
                 mDisplayContent.prepareAppTransition(TRANSIT_NONE);
                 mTaskSupervisor.mNoAnimActivities.add(top);
                 ActivityOptions.abort(options);
             } else {
-                updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT,
-                        options, false /* forceOverride */);
+                updateTransitLocked(TRANSIT_TO_FRONT, options);
             }
 
             // If a new task is moved to the front, then mark the existing top activity as
@@ -6979,8 +6950,7 @@
         if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
                 + tr.mTaskId);
 
-        mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_TO_BACK,
-                false /* alwaysKeepCurrent */);
+        mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
         mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
         moveToBack("moveTaskToBackLocked", tr);
 
@@ -7474,9 +7444,7 @@
 
     @Override
     void onChildPositionChanged(WindowContainer child) {
-        if (isOrganized()) {
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
-        }
+        dispatchTaskInfoChangedIfNeeded(false /* force */);
 
         if (!mChildren.contains(child)) {
             return;
@@ -7631,6 +7599,12 @@
         return super.getBounds();
     }
 
+    void dispatchTaskInfoChangedIfNeeded(boolean force) {
+        if (isOrganized()) {
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force);
+        }
+    }
+
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ed4c764..c02e7ad 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -657,6 +657,13 @@
                 }
             }
             return SCREEN_ORIENTATION_UNSPECIFIED;
+        } else {
+            // Apps and their containers are not allowed to specify an orientation of full screen
+            // tasks created by organizer. The organizer handles the orientation instead.
+            final Task task = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            if (task != null && task.isVisible() && task.mCreatedByOrganizer) {
+                return SCREEN_ORIENTATION_UNSPECIFIED;
+            }
         }
 
         final int orientation = super.getOrientation(candidate);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index e64c047..009a7ef 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -27,7 +27,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.TaskDescription;
 import android.app.WindowConfiguration;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -52,7 +51,6 @@
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
 
@@ -538,16 +536,7 @@
         }
         mTmpTaskInfo.configuration.unset();
         task.fillTaskInfo(mTmpTaskInfo);
-        boolean changed = lastInfo == null
-                || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
-                || mTmpTaskInfo.isResizeable != lastInfo.isResizeable
-                || !Objects.equals(
-                        mTmpTaskInfo.positionInParent,
-                        lastInfo.positionInParent)
-                || isLetterboxInfoChanged(lastInfo, mTmpTaskInfo)
-                || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
-                || mTmpTaskInfo.getWindowingMode() != lastInfo.getWindowingMode()
-                || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
+        boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
         if (!changed) {
             int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
             final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
@@ -582,20 +571,6 @@
         }
     }
 
-    private boolean isLetterboxInfoChanged(
-                final RunningTaskInfo lastInfo, final RunningTaskInfo currentInfo) {
-        return !Objects.equals(
-                        currentInfo.letterboxActivityBounds,
-                        lastInfo.letterboxActivityBounds)
-                || !Objects.equals(
-                        currentInfo.getConfiguration().windowConfiguration.getBounds(),
-                        lastInfo.getConfiguration().windowConfiguration.getBounds())
-                || !Objects.equals(
-                        currentInfo.getConfiguration().windowConfiguration.getMaxBounds(),
-                        lastInfo.getConfiguration().windowConfiguration.getMaxBounds())
-                || !Objects.equals(currentInfo.parentBounds, lastInfo.parentBounds);
-    }
-
     @Override
     public WindowContainerToken getImeTarget(int displayId) {
         enforceTaskPermission("getImeTarget()");
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 3327300..616a789 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -41,6 +41,7 @@
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.WindowInsets.Type;
+import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -333,7 +334,7 @@
         builder.setOrientation(activity.getTask().getConfiguration().orientation);
         builder.setRotation(activity.getTask().getDisplayContent().getRotation());
         builder.setWindowingMode(task.getWindowingMode());
-        builder.setSystemUiVisibility(getSystemUiVisibility(task));
+        builder.setAppearance(getAppearance(task));
         return true;
     }
 
@@ -507,7 +508,7 @@
                 hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
                 mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
                 contentInsets, false /* isLowResolution */, false /* isRealSnapshot */,
-                task.getWindowingMode(), getSystemUiVisibility(task), false);
+                task.getWindowingMode(), getAppearance(task), false);
     }
 
     /**
@@ -584,16 +585,16 @@
     }
 
     /**
-     * @return The SystemUI visibility flags for the top fullscreen opaque window in the given
+     * @return The {@link Appearance} flags for the top fullscreen opaque window in the given
      *         {@param task}.
      */
-    private int getSystemUiVisibility(Task task) {
+    private @Appearance int getAppearance(Task task) {
         final ActivityRecord topFullscreenActivity = task.getTopFullscreenActivity();
         final WindowState topFullscreenOpaqueWindow = topFullscreenActivity != null
                 ? topFullscreenActivity.getTopFullscreenOpaqueWindow()
                 : null;
         if (topFullscreenOpaqueWindow != null) {
-            return topFullscreenOpaqueWindow.getSystemUiVisibility();
+            return topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
         }
         return 0;
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 89ddc29..9717e7e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -197,7 +197,7 @@
                     hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
                     new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                     loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
-                    proto.systemUiVisibility, proto.isTranslucent);
+                    proto.appearance, proto.isTranslucent);
         } catch (IOException e) {
             Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
             return null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 324f0c2..6dfcb41 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -28,7 +28,6 @@
 import android.graphics.Bitmap.Config;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.UserManagerInternal;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -36,6 +35,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
 
 import java.io.File;
@@ -382,7 +382,7 @@
             proto.insetBottom = mSnapshot.getContentInsets().bottom;
             proto.isRealSnapshot = mSnapshot.isRealSnapshot();
             proto.windowingMode = mSnapshot.getWindowingMode();
-            proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
+            proto.appearance = mSnapshot.getAppearance();
             proto.isTranslucent = mSnapshot.isTranslucent();
             proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
             proto.id = mSnapshot.getId();
diff --git a/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java
index 7f62630..0e2b71c 100644
--- a/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java
+++ b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java
@@ -21,7 +21,9 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 
+import java.io.PrintWriter;
 import java.util.concurrent.Executor;
+import java.util.function.Predicate;
 
 /**
  * A quick lookup for all processes with visible activities. It also tracks the CPU usage of
@@ -70,10 +72,22 @@
     }
 
     boolean hasResumedActivity(int uid) {
+        return match(uid, WindowProcessController::hasResumedActivity);
+    }
+
+    /**
+     * Returns {@code true} if the uid has a process that contains an activity with
+     * {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true.
+     */
+    boolean hasVisibleActivity(int uid) {
+        return match(uid, null /* predicate */);
+    }
+
+    private boolean match(int uid, Predicate<WindowProcessController> predicate) {
         synchronized (mProcMap) {
             for (int i = mProcMap.size() - 1; i >= 0; i--) {
                 final WindowProcessController wpc = mProcMap.keyAt(i);
-                if (wpc.mUid == uid && wpc.hasResumedActivity()) {
+                if (wpc.mUid == uid && (predicate == null || predicate.test(wpc))) {
                     return true;
                 }
             }
@@ -87,6 +101,16 @@
         }
     }
 
+    void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix + "VisibleActivityProcess:[");
+        synchronized (mProcMap) {
+            for (int i = mProcMap.size() - 1; i >= 0; i--) {
+                pw.print(" " + mProcMap.keyAt(i));
+            }
+        }
+        pw.println("]");
+    }
+
     /**
      * Get CPU time in background thread because it will access proc files or the lock of cpu
      * tracker is held by a background thread.
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index ce13867..7d61c19 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -148,7 +148,7 @@
                 ? w.mActivityRecord.getAnimatingContainer() : null;
         final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null
                 && animatingContainer.isAnimating(TRANSITION | PARENTS)
-                && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit)
+                && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit)
                 && (animatingContainer.mTransitFlags
                 & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ff5c174..f627ca6 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -145,6 +145,9 @@
         ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
         mService.openSurfaceTransaction();
         try {
+            // Remove all deferred displays, tasks, and activities.
+            mService.mRoot.handleCompleteDeferredRemoval();
+
             final AccessibilityController accessibilityController =
                     mService.mAccessibilityController;
             final int numDisplays = mDisplayContentsAnimators.size();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 22dca26..4574be7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -81,6 +81,7 @@
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
+import android.view.WindowManager.TransitionOldType;
 import android.view.animation.Animation;
 import android.window.IWindowContainerToken;
 import android.window.WindowContainerToken;
@@ -250,10 +251,9 @@
     boolean mLaunchTaskBehind;
 
     /**
-     * If we are running an animation, this determines the transition type. Must be one of
-     * {@link AppTransition#TransitionFlags}.
+     * If we are running an animation, this determines the transition type.
      */
-    int mTransit;
+    @TransitionOldType int mTransit;
 
     /**
      * If we are running an animation, this determines the flags during this animation. Must be a
@@ -1112,7 +1112,7 @@
                 // descendant. E.g. if a display is pending to be removed because it contains an
                 // activity with {@link ActivityRecord#mIsExiting} is true, the display may be
                 // removed when completing the removal of the last activity from
-                // {@link ActivityRecord#checkCompleteDeferredRemoval}.
+                // {@link ActivityRecord#handleCompleteDeferredRemoval}.
                 return false;
             }
         }
@@ -2399,8 +2399,9 @@
      *
      * @see #getAnimationAdapter
      */
-    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-            boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
+    boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
+            boolean enter, boolean isVoiceInteraction,
+            @Nullable ArrayList<WindowContainer> sources) {
         if (mWmService.mDisableTransitionAnimation) {
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                     "applyAnimation: transition animation is disabled or skipped. "
@@ -2415,6 +2416,9 @@
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
             if (okToAnimate()) {
+                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+                        "applyAnimation: transit=%s, enter=%b, wc=%s",
+                        AppTransition.appTransitionOldToString(transit), enter, this);
                 applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
             } else {
                 cancelAnimation();
@@ -2437,7 +2441,7 @@
      * @See LocalAnimationAdapter
      */
     Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
-            int transit, boolean enter, boolean isVoiceInteraction) {
+            @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
         final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
         final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode();
 
@@ -2449,7 +2453,7 @@
 
         final RemoteAnimationController controller =
                 getDisplayContent().mAppTransition.getRemoteAnimationController();
-        final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
+        final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
                 && isChangingAppTransition();
 
         // Delaying animation start isn't compatible with remote animations at all.
@@ -2497,7 +2501,7 @@
 
                 resultAdapters = new Pair<>(adapter, null);
                 mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
-                        || AppTransition.isClosingTransit(transit);
+                        || AppTransition.isClosingTransitOld(transit);
                 mTransit = transit;
                 mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
             } else {
@@ -2508,7 +2512,7 @@
     }
 
     protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
-            int transit, boolean isVoiceInteraction,
+            @TransitionOldType int transit, boolean isVoiceInteraction,
             @Nullable ArrayList<WindowContainer> sources) {
         final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                 transit, enter, isVoiceInteraction);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8729801..d082778 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -115,9 +115,9 @@
         /**
          * Called when a pending app transition gets cancelled.
          *
-         * @param transit transition type indicating what kind of transition got cancelled
+         * @param keyguardGoingAway true if keyguard going away transition transition got cancelled.
          */
-        public void onAppTransitionCancelledLocked(int transit) {}
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {}
 
         /**
          * Called when an app transition is timed out.
@@ -127,8 +127,7 @@
         /**
          * Called when an app transition gets started
          *
-         * @param transit transition type indicating what kind of transition gets run, must be one
-         *                of AppTransition.TRANSIT_* values
+         * @param keyguardGoingAway true if keyguard going away transition is started.
          * @param duration the total duration of the transition
          * @param statusBarAnimationStartTime the desired start time for all visual animations in
          *        the status bar caused by this app transition in uptime millis
@@ -140,7 +139,7 @@
          * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
          * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
          */
-        public int onAppTransitionStartingLocked(int transit, long duration,
+        public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
                 long statusBarAnimationStartTime, long statusBarAnimationDuration) {
             return 0;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2e95fca..580c088 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -79,7 +79,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
@@ -423,16 +422,6 @@
             DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
 
     /**
-     * Use new app transit framework.
-     */
-    private static final String USE_NEW_APP_TRANSIT =
-            "persist.wm.use_new_app_transit";
-    /**
-     * @see #USE_NEW_APP_TRANSIT
-     */
-    static boolean sUseNewAppTransit = SystemProperties.getBoolean(USE_NEW_APP_TRANSIT, false);
-
-    /**
      * Allows a fullscreen windowing mode activity to launch in its desired orientation directly
      * when the display has different orientation.
      */
@@ -1111,7 +1100,7 @@
             = new WindowManagerInternal.AppTransitionListener() {
 
         @Override
-        public void onAppTransitionCancelledLocked(int transit) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
         }
 
         @Override
@@ -1923,8 +1912,6 @@
         // animation and piggy-back on existing transition animation infrastructure.
         final DisplayContent dc = activity.getDisplayContent();
         dc.mOpeningApps.add(activity);
-        dc.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH,
-                ALWAYS_KEEP_CURRENT, 0 /* flags */, false /* forceOverride */);
         dc.prepareAppTransition(TRANSIT_RELAUNCH);
         dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
                 frame.width(), frame.height());
@@ -1940,8 +1927,6 @@
         final DisplayContent dc = activity.getDisplayContent();
         if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) {
             dc.mOpeningApps.add(activity);
-            dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
-                    false /* forceOverride */);
             dc.prepareAppTransition(TRANSIT_NONE);
             dc.executeAppTransition();
         }
@@ -2212,15 +2197,12 @@
             if (attrs != null) {
                 displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                 win.mToken.adjustWindowParams(win, attrs);
-                int systemUiVisibility = attrs.systemUiVisibility
-                        | attrs.subtreeSystemUiVisibility;
-                if ((systemUiVisibility & DISABLE_MASK) != 0) {
-                    // if they don't have the permission, mask out the status bar bits
-                    if (!hasStatusBarPermission(pid, uid)) {
-                        systemUiVisibility &= ~DISABLE_MASK;
-                    }
+                int disableFlags =
+                        (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
+                if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
+                    disableFlags = 0;
                 }
-                win.mSystemUiVisibility = systemUiVisibility;
+                win.mDisableFlags = disableFlags;
                 if (win.mAttrs.type != attrs.type) {
                     throw new IllegalArgumentException(
                             "Window type can not be changed after the window is added.");
@@ -2830,9 +2812,6 @@
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        getDefaultDisplayContentLocked().prepareAppTransitionOld(TRANSIT_OLD_NONE,
-                false /* alwaysKeepCurrent */,
-                0 /* flags */, false /* forceOverride */);
         getDefaultDisplayContentLocked().prepareAppTransition(TRANSIT_NONE);
     }
 
@@ -2999,7 +2978,7 @@
                         displayContent, true /* includingParents */);
             }
         }
-        syncInputTransactions();
+        syncInputTransactions(true /* waitForAnimations */);
     }
 
     /**
@@ -8000,7 +7979,8 @@
     }
 
     @Override
-    public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
+    public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode,
+            boolean waitForAnimations) {
         boolean isDown;
         boolean isUp;
 
@@ -8019,21 +7999,23 @@
         // For all mouse events, also sync before injecting.
         // For ACTION_UP, sync after injecting.
         if (isDown || isMouseEvent) {
-            syncInputTransactions();
+            syncInputTransactions(waitForAnimations);
         }
         final boolean result =
                 LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode);
         if (isUp) {
-            syncInputTransactions();
+            syncInputTransactions(waitForAnimations);
         }
         return result;
     }
 
     @Override
-    public void syncInputTransactions() {
+    public void syncInputTransactions(boolean waitForAnimations) {
         final long token = Binder.clearCallingIdentity();
         try {
-            waitForAnimationsToComplete();
+            if (waitForAnimations) {
+                waitForAnimationsToComplete();
+            }
 
             // Collect all input transactions from all displays to make sure we could sync all input
             // windows at same time.
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 88fd361..c55f059 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -607,7 +607,7 @@
 
     private boolean isBoundByForegroundUid() {
         for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
-            if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) {
+            if (mAtm.hasActiveVisibleWindow(mBoundClientUids.valueAt(i))) {
                 return true;
             }
         }
@@ -1049,16 +1049,6 @@
                 & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = mActivities.get(i);
-            if (r.app != this) {
-                Slog.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
-                        + " instead of expected " + this);
-                if (r.app == null || (r.app.mUid == mUid)) {
-                    // Only fix things up when they look valid.
-                    r.setProcess(this);
-                } else {
-                    continue;
-                }
-            }
             if (r.isVisible()) {
                 stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
             }
@@ -1115,6 +1105,7 @@
     /** Called when the process has some oom related changes and it is going to update oom-adj. */
     private void prepareOomAdjustment() {
         mAtm.mRootWindowContainer.rankTaskLayersIfNeeded();
+        mAtm.mTaskSupervisor.computeProcessActivityStateBatch();
     }
 
     public int computeRelaunchReason() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d972a51..0a8ff4d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -176,7 +176,6 @@
 import static com.android.server.wm.WindowStateProto.STACK_ID;
 import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
 import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
-import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
@@ -231,6 +230,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
 import android.view.WindowInsets.Type.InsetsType;
@@ -307,7 +307,14 @@
     final boolean mIsWallpaper;
     private final boolean mIsFloatingLayer;
     int mViewVisibility;
-    int mSystemUiVisibility;
+
+    /**
+     * Flags to disable system UI functions. This can only be set by the one which has the
+     * status bar permission.
+     *
+     * @see View.SystemUiVisibility
+     */
+    int mDisableFlags;
 
     /**
      * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
@@ -1284,9 +1291,9 @@
         return mAttrs;
     }
 
-    /** Retrieves the current system UI visibility flags associated with this window. */
-    int getSystemUiVisibility() {
-        return mSystemUiVisibility;
+    /** Retrieves the flags used to disable system UI functions. */
+    int getDisableFlags() {
+        return mDisableFlags;
     }
 
     /** Gets the layer at which this window's surface will be Z-ordered. */
@@ -3279,6 +3286,11 @@
             logExclusionRestrictions(EXCLUSION_LEFT);
             logExclusionRestrictions(EXCLUSION_RIGHT);
         }
+        // Exclude toast because legacy apps may show toast window by themselves, so the misused
+        // apps won't always be considered as foreground state.
+        if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
+            mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
+        }
     }
 
     private void logExclusionRestrictions(int side) {
@@ -3889,7 +3901,6 @@
         proto.write(REQUESTED_WIDTH, mRequestedWidth);
         proto.write(REQUESTED_HEIGHT, mRequestedHeight);
         proto.write(VIEW_VISIBILITY, mViewVisibility);
-        proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
         proto.write(HAS_SURFACE, mHasSurface);
         proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
         proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
@@ -3965,8 +3976,10 @@
             pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
                     + " mHaveFrame=" + mHaveFrame
                     + " mObscured=" + mObscured);
-            pw.println(prefix
-                    + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
+            if (mDisableFlags != 0) {
+                pw.println(prefix + "mDisableFlags=" + ViewDebug.flagsToString(
+                        View.class, "mSystemUiVisibility", mDisableFlags));
+            }
         }
         if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
                 || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
@@ -5616,23 +5629,6 @@
                 true /* ignoreVisibility */));
     }
 
-    /**
-     * Returns {@code true} if this window is not {@link WindowManager.LayoutParams#TYPE_TOAST}
-     * or {@link WindowManager.LayoutParams#TYPE_APPLICATION_STARTING},
-     * since this window doesn't belong to apps.
-     */
-    boolean isNonToastOrStarting() {
-        return mAttrs.type != TYPE_TOAST && mAttrs.type != TYPE_APPLICATION_STARTING;
-    }
-
-    boolean isNonToastWindowVisibleForUid(int callingUid) {
-        return getOwningUid() == callingUid && isNonToastOrStarting() && isVisibleNow();
-    }
-
-    boolean isNonToastWindowVisibleForPid(int pid) {
-        return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow();
-    }
-
     void setViewVisibility(int viewVisibility) {
         mViewVisibility = viewVisibility;
     }
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index fb55e75..d1918d8 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -28,3 +28,10 @@
     api_dir: "cec-config/schema",
     package_name: "com.android.server.hdmi.cec.config",
 }
+
+xsd_config {
+    name: "device-state-config",
+    srcs: ["device-state-config/device-state-config.xsd"],
+    api_dir: "device-state-config/schema",
+    package_name: "com.android.server.policy.devicestate.config",
+}
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
new file mode 100644
index 0000000..0d8c08c
--- /dev/null
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -0,0 +1,78 @@
+<?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.
+  -->
+
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:element name="device-state-config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="device-state" type="deviceState" maxOccurs="256" />
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="deviceState">
+        <xs:sequence>
+            <xs:element name="identifier">
+                <xs:simpleType>
+                    <xs:restriction base="xs:integer">
+                        <xs:minInclusive value="0" />
+                        <xs:maxInclusive value="255" />
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:element>
+            <xs:element name="name" type="xs:string" minOccurs="0" />
+            <xs:element name="conditions" type="conditions" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="conditions">
+        <xs:sequence>
+            <xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0" />
+            <xs:element name="sensor" type="sensorCondition" minOccurs="0" maxOccurs="unbounded" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="lidSwitchCondition">
+        <xs:sequence>
+            <xs:element name="open" type="xs:boolean" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="sensorCondition">
+        <xs:sequence>
+            <xs:element name="name" type="xs:string" />
+            <xs:element name="type" type="xs:positiveInteger" />
+            <xs:element name="value" type="numericRange" maxOccurs="unbounded" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="numericRange">
+        <xs:sequence>
+            <xs:choice minOccurs="0">
+                <xs:element name="min" type="xs:decimal" />
+                <xs:element name="min-inclusive" type="xs:decimal" />
+            </xs:choice>
+            <xs:choice minOccurs="0">
+                <xs:element name="max" type="xs:decimal" />
+                <xs:element name="max-inclusive" type="xs:decimal"/>
+            </xs:choice>
+        </xs:sequence>
+    </xs:complexType>
+</xs:schema>
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
new file mode 100644
index 0000000..667d1ad
--- /dev/null
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -0,0 +1,61 @@
+// Signature format: 2.0
+package com.android.server.policy.devicestate.config {
+
+  public class Conditions {
+    ctor public Conditions();
+    method public com.android.server.policy.devicestate.config.LidSwitchCondition getLidSwitch();
+    method public java.util.List<com.android.server.policy.devicestate.config.SensorCondition> getSensor();
+    method public void setLidSwitch(com.android.server.policy.devicestate.config.LidSwitchCondition);
+  }
+
+  public class DeviceState {
+    ctor public DeviceState();
+    method public com.android.server.policy.devicestate.config.Conditions getConditions();
+    method public java.math.BigInteger getIdentifier();
+    method public String getName();
+    method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
+    method public void setIdentifier(java.math.BigInteger);
+    method public void setName(String);
+  }
+
+  public class DeviceStateConfig {
+    ctor public DeviceStateConfig();
+    method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
+  }
+
+  public class LidSwitchCondition {
+    ctor public LidSwitchCondition();
+    method public boolean getOpen();
+    method public void setOpen(boolean);
+  }
+
+  public class NumericRange {
+    ctor public NumericRange();
+    method public java.math.BigDecimal getMaxInclusive_optional();
+    method public java.math.BigDecimal getMax_optional();
+    method public java.math.BigDecimal getMinInclusive_optional();
+    method public java.math.BigDecimal getMin_optional();
+    method public void setMaxInclusive_optional(java.math.BigDecimal);
+    method public void setMax_optional(java.math.BigDecimal);
+    method public void setMinInclusive_optional(java.math.BigDecimal);
+    method public void setMin_optional(java.math.BigDecimal);
+  }
+
+  public class SensorCondition {
+    ctor public SensorCondition();
+    method public String getName();
+    method public java.math.BigInteger getType();
+    method public java.util.List<com.android.server.policy.devicestate.config.NumericRange> getValue();
+    method public void setName(String);
+    method public void setType(java.math.BigInteger);
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.policy.devicestate.config.DeviceStateConfig read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/services/core/xsd/device-state-config/schema/last_current.txt b/services/core/xsd/device-state-config/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/device-state-config/schema/last_current.txt
diff --git a/services/core/xsd/device-state-config/schema/last_removed.txt b/services/core/xsd/device-state-config/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/device-state-config/schema/last_removed.txt
diff --git a/services/core/xsd/device-state-config/schema/removed.txt b/services/core/xsd/device-state-config/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/device-state-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index bdb7f79..8a585ec 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -29,20 +29,17 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -80,6 +77,8 @@
     private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
             "device-provisioning-config-applied";
     private static final String ATTR_DEVICE_PAIRED = "device-paired";
+    private static final String TAG = DevicePolicyManagerService.LOG_TAG;
+    private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
 
     int mFailedPasswordAttempts = 0;
     boolean mPasswordValidAtLastCheckpoint = true;
@@ -155,7 +154,12 @@
     static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
         FileOutputStream stream = null;
         try {
-            stream = new FileOutputStream(file.chooseForWrite(), false);
+            File chooseForWrite = file.chooseForWrite();
+            if (VERBOSE_LOG) {
+                Slog.v(TAG, "Storing data for user " + policyData.mUserHandle + " on "
+                        + chooseForWrite);
+            }
+            stream = new FileOutputStream(chooseForWrite, false);
             TypedXmlSerializer out = Xml.resolveSerializer(stream);
             out.startDocument(null, true);
 
@@ -165,6 +169,7 @@
                         policyData.mRestrictionsProvider.flattenToString());
             }
             if (policyData.mUserSetupComplete) {
+                if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
                 out.attribute(null, ATTR_SETUP_COMPLETE,
                         Boolean.toString(true));
             }
@@ -348,7 +353,7 @@
             file.commit();
             return true;
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(DevicePolicyManagerService.LOG_TAG, "failed writing file", e);
+            Slog.w(TAG, "failed writing file", e);
             try {
                 if (stream != null) {
                     stream.close();
@@ -370,6 +375,9 @@
             ComponentName ownerComponent) {
         FileInputStream stream = null;
         File file = journaledFile.chooseForRead();
+        if (VERBOSE_LOG) {
+            Slog.v(TAG, "Loading data for user " + policy.mUserHandle + " from " + file);
+        }
         boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
@@ -393,6 +401,7 @@
             }
             String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
             if (Boolean.toString(true).equals(userSetupComplete)) {
+                if (VERBOSE_LOG) Slog.v(TAG, "setting mUserSetupComplete to true");
                 policy.mUserSetupComplete = true;
             }
             String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
@@ -443,8 +452,7 @@
                             policy.mAdminMap.put(ap.info.getComponent(), ap);
                         }
                     } catch (RuntimeException e) {
-                        Slog.w(DevicePolicyManagerService.LOG_TAG,
-                                "Failed loading admin " + name, e);
+                        Slog.w(TAG, "Failed loading admin " + name, e);
                     }
                 } else if ("delegation".equals(tag)) {
                     // Parse delegation info.
@@ -524,7 +532,7 @@
                     policy.mAppsSuspended =
                             Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
                 } else {
-                    Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown tag: " + tag);
+                    Slog.w(TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
@@ -532,7 +540,7 @@
             // Don't be noisy, this is normal if we haven't defined any policies.
         } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
                 | IndexOutOfBoundsException e) {
-            Slog.w(DevicePolicyManagerService.LOG_TAG, "failed parsing " + file, e);
+            Slog.w(TAG, "failed parsing " + file, e);
         }
         try {
             if (stream != null) {
@@ -557,8 +565,8 @@
                 }
             }
             if (!haveOwner) {
-                Slog.w(DevicePolicyManagerService.LOG_TAG, "Previous password owner "
-                        + mPasswordOwner + " no longer active; disabling");
+                Slog.w(TAG, "Previous password owner " + mPasswordOwner
+                        + " no longer active; disabling");
                 mPasswordOwner = -1;
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 20b6b6d..a646682 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,9 +91,6 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.os.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
-import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
-import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
@@ -108,6 +105,9 @@
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
 
 import android.Manifest.permission;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -222,8 +222,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
 import android.permission.IPermissionManager;
 import android.permission.PermissionControllerManager;
@@ -293,6 +291,8 @@
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.RestrictionsSet;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -4373,7 +4373,7 @@
             final int adminUser = admin.getUserHandle().getIdentifier();
             // Password complexity is only taken into account from DO/PO
             if (isDeviceOwner(adminComponent, adminUser)
-                    || isProfileOwner(adminComponent, adminUser)) {
+                    || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) {
                 maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
             }
         }
@@ -6216,7 +6216,7 @@
         }
         Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        final CallerIdentity caller = getAdminCallerIdentity(comp);
+        final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
         Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
@@ -7487,6 +7487,12 @@
         return who != null && who.equals(profileOwner);
     }
 
+    private boolean isProfileOwnerUncheckedLocked(ComponentName who, int userId) {
+        ensureLocked();
+        final ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+        return who != null && who.equals(profileOwner);
+    }
+
     /**
      * Returns {@code true} if the provided caller identity is of a profile owner.
      * @param caller identity of caller.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 9e98fc5..8ef6982 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -30,7 +30,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
@@ -46,6 +45,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import libcore.io.IoUtils;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 91478a5..eb6b325 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -481,6 +481,9 @@
         if (!mkdirOrLog(path::join(backing, ".index"), 0777)) {
             return kInvalidStorageId;
         }
+        if (!mkdirOrLog(path::join(backing, ".incomplete"), 0777)) {
+            return kInvalidStorageId;
+        }
         auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
         if (!status.isOk()) {
             LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 27b07c7..21c863d 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName
 import android.content.Context
+import android.content.pm.PackageManager
 import android.content.pm.parsing.component.ParsedActivity
 import android.os.Binder
 import android.os.UserHandle
@@ -31,7 +32,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.parsing.pkg.ParsedPackage
-import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
@@ -45,11 +45,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import org.mockito.Mockito
 import org.mockito.Mockito.any
-import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.anyString
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.intThat
 import org.mockito.Mockito.never
@@ -321,10 +318,6 @@
             whenever(this.exists(intThat(matcher))) { true }
             whenever(this.isUserUnlockingOrUnlocked(intThat(matcher))) { true }
         }
-        val mockPermissionManagerService: PermissionManagerServiceInternal = mockThrowOnUnmocked {
-            whenever(this.enforceCrossUserPermission(anyInt(), anyInt(), anyBoolean(), anyBoolean(),
-                    anyString())) { }
-        }
         val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked {
             whenever(this.isCallerRecents(anyInt())) { false }
         }
@@ -335,15 +328,19 @@
         val mockContext: Context = mockThrowOnUnmocked {
             whenever(this.getString(
                     com.android.internal.R.string.config_overrideComponentUiPackage)) { VALID_PKG }
+            whenever(this.checkCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+                PackageManager.PERMISSION_GRANTED
+            }
         }
         val mockInjector: PackageManagerService.Injector = mock {
             whenever(this.lock) { Object() }
             whenever(this.componentResolver) { mockComponentResolver }
             whenever(this.userManagerService) { mockUserManagerService }
-            whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService }
             whenever(this.settings) { mockSettings }
             whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) {
-                mockActivityTaskManager}
+                mockActivityTaskManager
+            }
             whenever(this.appsFilter) { mockAppsFilter }
             whenever(this.context) { mockContext }
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 77fef12..8795d77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -284,6 +284,8 @@
                 return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
             case RARE_INDEX:
                 return UsageStatsManager.STANDBY_BUCKET_RARE;
+            case RESTRICTED_INDEX:
+                return UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
             default:
                 return UsageStatsManager.STANDBY_BUCKET_NEVER;
         }
@@ -292,6 +294,7 @@
     private void setStandbyBucket(int bucketIndex) {
         when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID),
                 anyLong())).thenReturn(bucketIndexToUsageStatsBucket(bucketIndex));
+        mQuotaController.updateStandbyBucket(SOURCE_USER_ID, SOURCE_PACKAGE, bucketIndex);
     }
 
     private void setStandbyBucket(int bucketIndex, JobStatus... jobs) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java
index be6bc99..ac23d4e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java
@@ -100,6 +100,11 @@
     }
 
     @Override
+    public int getCurrentUserId() {
+        return mCurrentUserId;
+    }
+
+    @Override
     protected int[] getProfileIds(int userId) {
         IntArray profiles = mProfiles.get(userId);
         if (profiles != null) {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1f72374..f2bb91c 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -103,6 +103,7 @@
         ":PackageParserTestApp1",
         ":PackageParserTestApp2",
         ":PackageParserTestApp3",
+        ":PackageParserTestApp4",
         ":apex.test",
     ],
     resource_zips: [":FrameworksServicesTests_apks_as_resources"],
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index e76c5a4..a02c533 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.never;
@@ -36,6 +37,7 @@
 import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.test.mock.MockContentResolver;
+import android.testing.TestableLooper;
 import android.util.MutableBoolean;
 import android.view.KeyEvent;
 
@@ -44,6 +46,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -65,6 +68,7 @@
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GestureLauncherServiceTest {
 
     private static final int FAKE_USER_ID = 1337;
@@ -83,6 +87,7 @@
     private @Mock StatusBarManagerInternal mStatusBarManagerInternal;
     private @Mock TelecomManager mTelecomManager;
     private @Mock MetricsLogger mMetricsLogger;
+    @Mock private UiEventLogger mUiEventLogger;
     private MockContentResolver mContentResolver;
     private GestureLauncherService mGestureLauncherService;
 
@@ -109,7 +114,8 @@
         when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
         when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent());
 
-        mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger);
+        mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger,
+                mUiEventLogger);
     }
 
     @Test
@@ -268,6 +274,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -312,6 +319,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -358,6 +366,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -406,6 +415,8 @@
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
             .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -460,6 +471,8 @@
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
             .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
         final ArgumentCaptor<Integer> cameraIntervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -499,7 +512,8 @@
         assertTrue(intercepted);
         assertTrue(outLaunched.value);
 
-        // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER);
         verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -551,7 +565,8 @@
         assertTrue(outLaunched.value);
         assertTrue(intercepted);
 
-        // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER);
         verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -646,6 +661,7 @@
 
         verify(mMetricsLogger, never())
                 .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(1)).histogram(
@@ -690,6 +706,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -736,6 +753,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -782,6 +800,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -826,6 +845,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -869,6 +889,7 @@
         assertFalse(outLaunched.value);
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -914,6 +935,7 @@
         assertFalse(outLaunched.value);
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -961,6 +983,8 @@
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(mMetricsLogger)
             .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -1007,6 +1031,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -1051,6 +1076,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
@@ -1097,6 +1123,7 @@
 
         verify(mMetricsLogger, never())
             .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
 
         final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mMetricsLogger, times(2)).histogram(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 1cdd873..e43a002 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -66,7 +66,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.IntConsumer;
@@ -129,6 +128,8 @@
     ScaleChangedListener mMockScaleChangedListener;
     @Mock
     MagnificationRequestObserver mMagnificationRequestObserver;
+    @Mock
+    WindowMagnificationPromptController mWindowMagnificationPromptController;
 
     private OffsettableClock mClock;
     private FullScreenMagnificationGestureHandler mMgh;
@@ -170,7 +171,9 @@
 
     @After
     public void tearDown() {
+        mMgh.onDestroy();
         mFullScreenMagnificationController.unregister(DISPLAY_0);
+        verify(mWindowMagnificationPromptController).onDestroy();
     }
 
     @NonNull
@@ -178,7 +181,8 @@
             boolean detectShortcutTrigger) {
         FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
                 mContext, mFullScreenMagnificationController, mMockScaleChangedListener,
-                detectTripleTap, detectShortcutTrigger, DISPLAY_0);
+                detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController,
+                DISPLAY_0);
         mHandler = new TestHandler(h.mDetectingState, mClock) {
             @Override
             protected String messageToString(Message m) {
@@ -434,6 +438,20 @@
         returnToNormalFrom(STATE_PANNING);
     }
 
+    @Test
+    public void testZoomedWithTripleTap_invokeShowWindowPromptAction() {
+        goFromStateIdleTo(STATE_ZOOMED);
+
+        verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
+    }
+
+    @Test
+    public void testShortcutTriggered_invokeShowWindowPromptAction() {
+        goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+
+        verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
+    }
+
     private void assertActionsInOrder(List<MotionEvent> actualEvents,
             List<Integer> expectedActions) {
         assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
new file mode 100644
index 0000000..5fd28f57
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT;
+
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE;
+import static com.android.server.accessibility.magnification.WindowMagnificationPromptController.ACTION_TURN_ON_IN_SETTINGS;
+
+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.verify;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowMagnificationPromptController}.
+ */
+public class WindowMagnificationPromptControllerTest {
+
+    private static final int TEST_USER = 0;
+
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private StatusBarManager mStatusBarManager;
+    @Rule
+    public A11yTestableContext mTestableContext = new A11yTestableContext(
+            InstrumentationRegistry.getContext());
+    private ContentResolver mResolver = mTestableContext.getContentResolver();
+    private WindowMagnificationPromptController mWindowMagnificationPromptController;
+    private BroadcastReceiver mReceiver;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTestableContext.addMockSystemService(NotificationManager.class, mNotificationManager);
+        mTestableContext.addMockSystemService(StatusBarManager.class, mStatusBarManager);
+        setWindowMagnificationPromptSettings(true);
+        mWindowMagnificationPromptController = new WindowMagnificationPromptController(
+                mTestableContext, TEST_USER);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mWindowMagnificationPromptController.onDestroy();
+    }
+
+    @Test
+    public void showNotificationIfNeeded_promptSettingsIsOn_showNotification() {
+        mWindowMagnificationPromptController.showNotificationIfNeeded();
+
+        verify(mNotificationManager).notify(eq(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE), any(
+                Notification.class));
+    }
+
+    @Test
+    public void tapTurnOnAction_isShown_cancelNotificationAndLaunchMagnificationSettings() {
+        showNotificationAndAssert();
+
+        final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS);
+        mReceiver.onReceive(mTestableContext, intent);
+
+        verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+        verifyLaunchMagnificationSettings();
+    }
+
+    @Test
+    public void tapTurnOnAction_isShown_settingsValueIsFalseAndUnregisterReceiver() {
+        showNotificationAndAssert();
+
+        final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS);
+        mReceiver.onReceive(mTestableContext, intent);
+
+        assertThat(Settings.Secure.getInt(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+                -1)).isEqualTo(0);
+        verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+    }
+
+    @Test
+    public void tapDismissAction_isShown_cancelNotificationAndUnregisterReceiver() {
+        showNotificationAndAssert();
+
+        final Intent intent = new Intent(WindowMagnificationPromptController.ACTION_DISMISS);
+        mReceiver.onReceive(mTestableContext, intent);
+
+        verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+        verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+    }
+
+    @Test
+    public void promptSettingsChangeToFalse_isShown_cancelNotificationAndUnregisterReceiver() {
+        showNotificationAndAssert();
+
+        setWindowMagnificationPromptSettings(false);
+
+        verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+        verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+    }
+
+    @Test
+    public void onDestroy_isShown_cancelNotificationAndUnregisterReceiver() {
+        showNotificationAndAssert();
+
+        mWindowMagnificationPromptController.onDestroy();
+
+        verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+        verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+    }
+
+    private void verifyLaunchMagnificationSettings() {
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(
+                UserHandle.class);
+        verify(mTestableContext.getSpyContext()).startActivityAsUser(intentCaptor.capture(),
+                bundleCaptor.capture(), userHandleCaptor.capture());
+        assertThat(intentCaptor.getValue().getAction()).isEqualTo(
+                Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+        assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER);
+        verify(mStatusBarManager).collapsePanels();
+    }
+
+    private void showNotificationAndAssert() {
+        mWindowMagnificationPromptController.showNotificationIfNeeded();
+        mReceiver = mWindowMagnificationPromptController.mNotificationActionReceiver;
+        assertThat(mReceiver).isNotNull();
+    }
+
+    private void setWindowMagnificationPromptSettings(boolean enable) {
+        Settings.Secure.putIntForUser(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+                enable ? 1 : 0, TEST_USER);
+        if (mWindowMagnificationPromptController != null) {
+            mWindowMagnificationPromptController.onPromptSettingsValueChanged();
+        }
+    }
+
+    private class A11yTestableContext extends TestableContext {
+
+        private Context mSpyContext;
+
+        A11yTestableContext(Context base) {
+            super(base);
+            mSpyContext = Mockito.mock(Context.class);
+        }
+
+        @Override
+        public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+            mSpyContext.startActivityAsUser(intent, options, user);
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+                String broadcastPermission, Handler scheduler) {
+            return mSpyContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+        }
+
+        @Override
+        public void unregisterReceiver(BroadcastReceiver receiver) {
+            mSpyContext.unregisterReceiver(receiver);
+        }
+
+        Context getSpyContext() {
+            return mSpyContext;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index f45cc3b..74c6a7e 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -71,7 +71,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
@@ -80,6 +79,7 @@
 
 import com.android.server.FgThread;
 import com.android.server.am.UserState.KeyEvictedCallback;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 726e48a..b929061 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -26,15 +26,17 @@
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
 
+import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
 import com.android.server.appsearch.proto.DocumentProto;
 import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
-import com.android.server.appsearch.proto.IndexingConfig;
 import com.android.server.appsearch.proto.PropertyConfigProto;
 import com.android.server.appsearch.proto.PropertyProto;
 import com.android.server.appsearch.proto.SchemaProto;
 import com.android.server.appsearch.proto.SchemaTypeConfigProto;
 import com.android.server.appsearch.proto.SearchSpecProto;
+import com.android.server.appsearch.proto.StringIndexingConfig;
 import com.android.server.appsearch.proto.TermMatchType;
+
 import com.google.common.collect.ImmutableSet;
 
 import org.junit.Before;
@@ -42,18 +44,36 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 public class AppSearchImplTest {
-    @Rule
-    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
     private AppSearchImpl mAppSearchImpl;
+    private SchemaTypeConfigProto mVisibilitySchemaProto;
 
     @Before
     public void setUp() throws Exception {
         mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+
+        AppSearchSchema visibilityAppSearchSchema =
+                new AppSearchSchema.Builder(
+                                VisibilityStore.DATABASE_NAME
+                                        + AppSearchImpl.DATABASE_DELIMITER
+                                        + VisibilityStore.SCHEMA_TYPE)
+                        .addProperty(
+                                new AppSearchSchema.PropertyConfig.Builder(
+                                                VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
+                                        .setDataType(
+                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .build())
+                        .build();
+        mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema);
     }
 
     /**
@@ -62,91 +82,217 @@
      * schema.
      */
     @Test
-    public void testRewriteSchema() throws Exception {
-        SchemaProto.Builder existingSchemaBuilder = mAppSearchImpl.getSchemaProto().toBuilder();
+    public void testRewriteSchema_addType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("existingDatabase/Foo")
+                                        .build());
 
-        SchemaProto newSchema = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("Foo").build())
-                .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("TestType")
-                        .addProperties(PropertyConfigProto.newBuilder()
-                                .setPropertyName("subject")
-                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setIndexingConfig(
-                                        IndexingConfig.newBuilder()
-                                                .setTokenizerType(
-                                                        IndexingConfig.TokenizerType.Code.PLAIN)
-                                                .setTermMatchType(TermMatchType.Code.PREFIX)
-                                                .build()
-                                ).build()
-                        ).addProperties(PropertyConfigProto.newBuilder()
-                                .setPropertyName("link")
-                                .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
-                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setSchemaType("RefType")
-                                .build()
-                        ).build()
-                ).build();
+        // Create a copy so we can modify it.
+        List<SchemaTypeConfigProto> existingTypes =
+                new ArrayList<>(existingSchemaBuilder.getTypesList());
 
-        Set<String> newTypes = mAppSearchImpl.rewriteSchema("databaseName", existingSchemaBuilder,
-                newSchema);
-        assertThat(newTypes).containsExactly("databaseName/Foo", "databaseName/TestType");
+        SchemaProto newSchema =
+                SchemaProto.newBuilder()
+                        .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("TestType")
+                                        .addProperties(
+                                                PropertyConfigProto.newBuilder()
+                                                        .setPropertyName("subject")
+                                                        .setDataType(
+                                                                PropertyConfigProto.DataType.Code
+                                                                        .STRING)
+                                                        .setCardinality(
+                                                                PropertyConfigProto.Cardinality.Code
+                                                                        .OPTIONAL)
+                                                        .setStringIndexingConfig(
+                                                                StringIndexingConfig.newBuilder()
+                                                                        .setTokenizerType(
+                                                                                StringIndexingConfig
+                                                                                        .TokenizerType
+                                                                                        .Code.PLAIN)
+                                                                        .setTermMatchType(
+                                                                                TermMatchType.Code
+                                                                                        .PREFIX)
+                                                                        .build())
+                                                        .build())
+                                        .addProperties(
+                                                PropertyConfigProto.newBuilder()
+                                                        .setPropertyName("link")
+                                                        .setDataType(
+                                                                PropertyConfigProto.DataType.Code
+                                                                        .DOCUMENT)
+                                                        .setCardinality(
+                                                                PropertyConfigProto.Cardinality.Code
+                                                                        .OPTIONAL)
+                                                        .setSchemaType("RefType")
+                                                        .build())
+                                        .build())
+                        .build();
 
-        SchemaProto expectedSchema = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("databaseName/Foo").build())
-                .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("databaseName/TestType")
-                        .addProperties(PropertyConfigProto.newBuilder()
-                                .setPropertyName("subject")
-                                .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setIndexingConfig(
-                                        IndexingConfig.newBuilder()
-                                                .setTokenizerType(
-                                                        IndexingConfig.TokenizerType.Code.PLAIN)
-                                                .setTermMatchType(TermMatchType.Code.PREFIX)
-                                                .build()
-                                ).build()
-                        ).addProperties(PropertyConfigProto.newBuilder()
-                                .setPropertyName("link")
-                                .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
-                                .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setSchemaType("databaseName/RefType")
-                                .build()
-                        ).build())
-                .build();
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
+                mAppSearchImpl.rewriteSchema("newDatabase", existingSchemaBuilder, newSchema);
+
+        // We rewrote all the new types that were added. And nothing was removed.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("newDatabase/Foo", "newDatabase/TestType");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+
+        SchemaProto expectedSchema =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("newDatabase/Foo")
+                                        .build())
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("newDatabase/TestType")
+                                        .addProperties(
+                                                PropertyConfigProto.newBuilder()
+                                                        .setPropertyName("subject")
+                                                        .setDataType(
+                                                                PropertyConfigProto.DataType.Code
+                                                                        .STRING)
+                                                        .setCardinality(
+                                                                PropertyConfigProto.Cardinality.Code
+                                                                        .OPTIONAL)
+                                                        .setStringIndexingConfig(
+                                                                StringIndexingConfig.newBuilder()
+                                                                        .setTokenizerType(
+                                                                                StringIndexingConfig
+                                                                                        .TokenizerType
+                                                                                        .Code.PLAIN)
+                                                                        .setTermMatchType(
+                                                                                TermMatchType.Code
+                                                                                        .PREFIX)
+                                                                        .build())
+                                                        .build())
+                                        .addProperties(
+                                                PropertyConfigProto.newBuilder()
+                                                        .setPropertyName("link")
+                                                        .setDataType(
+                                                                PropertyConfigProto.DataType.Code
+                                                                        .DOCUMENT)
+                                                        .setCardinality(
+                                                                PropertyConfigProto.Cardinality.Code
+                                                                        .OPTIONAL)
+                                                        .setSchemaType("newDatabase/RefType")
+                                                        .build())
+                                        .build())
+                        .build();
+
+        existingTypes.addAll(expectedSchema.getTypesList());
+        assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes);
+    }
+
+    /**
+     * Ensure that we track all types that were rewritten in the input schema. Even if they were not
+     * technically "added" to the existing schema.
+     */
+    @Test
+    public void testRewriteSchema_rewriteType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("existingDatabase/Foo")
+                                        .build());
+
+        SchemaProto newSchema =
+                SchemaProto.newBuilder()
+                        .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
+                        .build();
+
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
+                mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema);
+
+        // Nothing was removed, but the method did rewrite the type name.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("existingDatabase/Foo");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+
+        // Same schema since nothing was added.
+        SchemaProto expectedSchema = existingSchemaBuilder.build();
+        assertThat(existingSchemaBuilder.getTypesList())
+                .containsExactlyElementsIn(expectedSchema.getTypesList());
+    }
+
+    /**
+     * Ensure that we track which types from the existing schema are deleted when a new schema is
+     * set.
+     */
+    @Test
+    public void testRewriteSchema_deleteType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("existingDatabase/Foo")
+                                        .build());
+
+        SchemaProto newSchema =
+                SchemaProto.newBuilder()
+                        .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build())
+                        .build();
+
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
+                mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema);
+
+        // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
+        // new schema.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("existingDatabase/Bar");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes)
+                .containsExactly("existingDatabase/Foo");
+
+        // Same schema since nothing was added.
+        SchemaProto expectedSchema =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("existingDatabase/Bar")
+                                        .build())
+                        .build();
+
         assertThat(existingSchemaBuilder.getTypesList())
                 .containsExactlyElementsIn(expectedSchema.getTypesList());
     }
 
     @Test
     public void testAddDocumentTypePrefix() {
-        DocumentProto insideDocument = DocumentProto.newBuilder()
-                .setUri("inside-uri")
-                .setSchema("type")
-                .setNamespace("namespace")
-                .build();
-        DocumentProto documentProto = DocumentProto.newBuilder()
-                .setUri("uri")
-                .setSchema("type")
-                .setNamespace("namespace")
-                .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
-                .build();
+        DocumentProto insideDocument =
+                DocumentProto.newBuilder()
+                        .setUri("inside-uri")
+                        .setSchema("type")
+                        .setNamespace("namespace")
+                        .build();
+        DocumentProto documentProto =
+                DocumentProto.newBuilder()
+                        .setUri("uri")
+                        .setSchema("type")
+                        .setNamespace("namespace")
+                        .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
+                        .build();
 
-        DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
-                .setUri("inside-uri")
-                .setSchema("databaseName/type")
-                .setNamespace("databaseName/namespace")
-                .build();
-        DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
-                .setUri("uri")
-                .setSchema("databaseName/type")
-                .setNamespace("databaseName/namespace")
-                .addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
-                .build();
+        DocumentProto expectedInsideDocument =
+                DocumentProto.newBuilder()
+                        .setUri("inside-uri")
+                        .setSchema("databaseName/type")
+                        .setNamespace("databaseName/namespace")
+                        .build();
+        DocumentProto expectedDocumentProto =
+                DocumentProto.newBuilder()
+                        .setUri("uri")
+                        .setSchema("databaseName/type")
+                        .setNamespace("databaseName/namespace")
+                        .addProperties(
+                                PropertyProto.newBuilder()
+                                        .addDocumentValues(expectedInsideDocument))
+                        .build();
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
         mAppSearchImpl.addPrefixToDocument(actualDocument, "databaseName/");
@@ -154,31 +300,37 @@
     }
 
     @Test
-    public void testRemoveDocumentTypePrefixes() {
-        DocumentProto insideDocument = DocumentProto.newBuilder()
-                .setUri("inside-uri")
-                .setSchema("databaseName1/type")
-                .setNamespace("databaseName2/namespace")
-                .build();
-        DocumentProto documentProto = DocumentProto.newBuilder()
-                .setUri("uri")
-                .setSchema("databaseName2/type")
-                .setNamespace("databaseName3/namespace")
-                .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
-                .build();
+    public void testRemoveDocumentTypePrefixes() throws Exception {
+        DocumentProto insideDocument =
+                DocumentProto.newBuilder()
+                        .setUri("inside-uri")
+                        .setSchema("databaseName1/type")
+                        .setNamespace("databaseName2/namespace")
+                        .build();
+        DocumentProto documentProto =
+                DocumentProto.newBuilder()
+                        .setUri("uri")
+                        .setSchema("databaseName2/type")
+                        .setNamespace("databaseName3/namespace")
+                        .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
+                        .build();
 
-        DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
-                .setUri("inside-uri")
-                .setSchema("type")
-                .setNamespace("namespace")
-                .build();
+        DocumentProto expectedInsideDocument =
+                DocumentProto.newBuilder()
+                        .setUri("inside-uri")
+                        .setSchema("type")
+                        .setNamespace("namespace")
+                        .build();
         // Since we don't pass in "databaseName3/" as a prefix to remove, it stays on the Document.
-        DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
-                .setUri("uri")
-                .setSchema("type")
-                .setNamespace("namespace")
-                .addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
-                .build();
+        DocumentProto expectedDocumentProto =
+                DocumentProto.newBuilder()
+                        .setUri("uri")
+                        .setSchema("type")
+                        .setNamespace("namespace")
+                        .addProperties(
+                                PropertyProto.newBuilder()
+                                        .addDocumentValues(expectedInsideDocument))
+                        .build();
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
         mAppSearchImpl.removeDatabasesFromDocument(actualDocument);
@@ -190,19 +342,23 @@
         // Insert schema
         Set<AppSearchSchema> schemas =
                 Collections.singleton(new AppSearchSchema.Builder("type").build());
-        mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false);
+        mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
 
         // Insert enough documents.
-        for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
-                + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
+        for (int i = 0;
+                i
+                        < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+                                + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
+                i++) {
             GenericDocument document =
-                    new GenericDocument.Builder("uri" + i, "type").setNamespace(
-                            "namespace").build();
+                    new GenericDocument.Builder("uri" + i, "type")
+                            .setNamespace("namespace")
+                            .build();
             mAppSearchImpl.putDocument("database", document);
         }
 
         // Check optimize() will release 0 docs since there is no deletion.
-        GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResult();
+        GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
         assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0);
 
         // delete 999 documents , we will reach the threshold to trigger optimize() in next
@@ -212,82 +368,82 @@
         }
 
         // optimize() still not be triggered since we are in the interval to call getOptimizeInfo()
-        optimizeInfo = mAppSearchImpl.getOptimizeInfoResult();
+        optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
         assertThat(optimizeInfo.getOptimizableDocs())
                 .isEqualTo(AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1);
 
         // Keep delete docs, will reach the interval this time and trigger optimize().
         for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
-                i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
-                        + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
+                i
+                        < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+                                + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
+                i++) {
             mAppSearchImpl.remove("database", "namespace", "uri" + i);
         }
 
         // Verify optimize() is triggered
-        optimizeInfo = mAppSearchImpl.getOptimizeInfoResult();
+        optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
         assertThat(optimizeInfo.getOptimizableDocs())
                 .isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL);
     }
 
     @Test
-    public void testRewriteSearchSpec_OneInstance() throws Exception {
-        SearchSpecProto.Builder searchSpecProto =
-                SearchSpecProto.newBuilder().setQuery("");
+    public void testRewriteSearchSpec_oneInstance() throws Exception {
+        SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
 
         // Insert schema
         Set<AppSearchSchema> schemas =
                 Collections.singleton(new AppSearchSchema.Builder("type").build());
-        mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false);
+        mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
 
         // Insert document
-        GenericDocument document = new GenericDocument.Builder("uri", "type").setNamespace(
-                "namespace").build();
+        GenericDocument document =
+                new GenericDocument.Builder("uri", "type").setNamespace("namespace").build();
         mAppSearchImpl.putDocument("database", document);
 
         // Rewrite SearchSpec
-        mAppSearchImpl.rewriteSearchSpecForDatabases(searchSpecProto, Collections.singleton(
-                "database"));
+        mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(
+                searchSpecProto, Collections.singleton("database"));
         assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly("database/type");
         assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly("database/namespace");
     }
 
     @Test
-    public void testRewriteSearchSpec_TwoInstances() throws Exception {
-        SearchSpecProto.Builder searchSpecProto =
-                SearchSpecProto.newBuilder().setQuery("");
+    public void testRewriteSearchSpec_twoInstances() throws Exception {
+        SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
 
         // Insert schema
-        Set<AppSearchSchema> schemas = Set.of(
-                new AppSearchSchema.Builder("typeA").build(),
-                new AppSearchSchema.Builder("typeB").build());
-        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
-        mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
+        Set<AppSearchSchema> schemas =
+                Set.of(
+                        new AppSearchSchema.Builder("typeA").build(),
+                        new AppSearchSchema.Builder("typeB").build());
+        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
 
         // Insert documents
-        GenericDocument document1 = new GenericDocument.Builder("uri", "typeA").setNamespace(
-                "namespace").build();
+        GenericDocument document1 =
+                new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build();
         mAppSearchImpl.putDocument("database1", document1);
 
-        GenericDocument document2 = new GenericDocument.Builder("uri", "typeB").setNamespace(
-                "namespace").build();
+        GenericDocument document2 =
+                new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build();
         mAppSearchImpl.putDocument("database2", document2);
 
         // Rewrite SearchSpec
-        mAppSearchImpl.rewriteSearchSpecForDatabases(searchSpecProto,
-                ImmutableSet.of("database1", "database2"));
-        assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
-                "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
-        assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
-                "database1/namespace", "database2/namespace");
+        mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(
+                searchSpecProto, ImmutableSet.of("database1", "database2"));
+        assertThat(searchSpecProto.getSchemaTypeFiltersList())
+                .containsExactly(
+                        "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
+        assertThat(searchSpecProto.getNamespaceFiltersList())
+                .containsExactly("database1/namespace", "database2/namespace");
     }
 
     @Test
     public void testQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.query(
-                "EmptyDatabase",
-                "", searchSpec);
+        SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -295,25 +451,25 @@
     public void testGlobalQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.query(
-                "EmptyDatabase",
-                "", searchSpec);
+        SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
     @Test
-    public void testRemoveEmptyDatabase_NoExceptionThrown() throws Exception {
+    public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
         SearchSpec searchSpec =
-                new SearchSpec.Builder().addSchema("FakeType").setTermMatch(
-                        TermMatchType.Code.PREFIX_VALUE).build();
-        mAppSearchImpl.removeByQuery("EmptyDatabase",
-                "", searchSpec);
+                new SearchSpec.Builder()
+                        .addSchemaType("FakeType")
+                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                        .build();
+        mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
 
         searchSpec =
-                new SearchSpec.Builder().addNamespace("FakeNamespace").setTermMatch(
-                        TermMatchType.Code.PREFIX_VALUE).build();
-        mAppSearchImpl.removeByQuery("EmptyDatabase",
-                "", searchSpec);
+                new SearchSpec.Builder()
+                        .addNamespace("FakeNamespace")
+                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                        .build();
+        mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
 
         searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
@@ -324,14 +480,46 @@
         Set<AppSearchSchema> schemas =
                 Collections.singleton(new AppSearchSchema.Builder("Email").build());
         // Set schema Email to AppSearch database1
-        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
+        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .build();
-        assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+        // Create expected schemaType proto.
+        SchemaProto expectedProto =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                        .build();
+
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(expectedTypes);
+    }
+
+    @Test
+    public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+                /*forceOverride=*/ false);
+        mAppSearchImpl.setVisibility("database", Set.of("schema1"));
+
+        // "schema1" is platform hidden now
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .containsExactly("database/schema1");
+
+        // Add a new schema, and include the already-existing "schema1"
+        mAppSearchImpl.setSchema(
+                "database",
+                Set.of(
+                        new AppSearchSchema.Builder("schema1").build(),
+                        new AppSearchSchema.Builder("schema2").build()),
+                /*forceOverride=*/ false);
+
+        // Check that "schema1" is still platform hidden, but "schema2" is the default platform
+        // visible.
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .containsExactly("database/schema1");
     }
 
     @Test
@@ -340,35 +528,52 @@
         schemas.add(new AppSearchSchema.Builder("Email").build());
         schemas.add(new AppSearchSchema.Builder("Document").build());
         // Set schema Email and Document to AppSearch database1
-        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
+        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
-                .build();
+        // Create expected schemaType proto.
+        SchemaProto expectedProto =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("database1/Document"))
+                        .build();
 
         // Check both schema Email and Document saved correctly.
-        assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(expectedTypes);
 
-        final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder(
-                "Email").build());
+        final Set<AppSearchSchema> finalSchemas =
+                Collections.singleton(new AppSearchSchema.Builder("Email").build());
         // Check the incompatible error has been thrown.
-        AppSearchException e = expectThrows(AppSearchException.class, () ->
-                mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/false));
+        AppSearchException e =
+                expectThrows(
+                        AppSearchException.class,
+                        () ->
+                                mAppSearchImpl.setSchema(
+                                        "database1", finalSchemas, /*forceOverride=*/ false));
         assertThat(e).hasMessageThat().contains("Schema is incompatible");
         assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
 
         // ForceOverride to delete.
-        mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/true);
+        mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true);
 
         // Check Document schema is removed.
-        exceptedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .build();
-        assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+        expectedProto =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                        .build();
+
+        expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(expectedTypes);
     }
 
     @Test
@@ -379,35 +584,156 @@
         schemas.add(new AppSearchSchema.Builder("Document").build());
 
         // Set schema Email and Document to AppSearch database1 and 2
-        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
-        mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
+        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
-                .build();
+        // Create expected schemaType proto.
+        SchemaProto expectedProto =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("database1/Document"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("database2/Document"))
+                        .build();
 
         // Check Email and Document is saved in database 1 and 2 correctly.
-        assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(expectedTypes);
 
         // Save only Email to database1 this time.
         schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
-        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/true);
+        mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true);
 
-        // Create excepted schemaType list, database 1 should only contain Email but database 2
+        // Create expected schemaType list, database 1 should only contain Email but database 2
         // remains in same.
-        exceptedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
-                .build();
+        expectedProto =
+                SchemaProto.newBuilder()
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
+                        .addTypes(
+                                SchemaTypeConfigProto.newBuilder()
+                                        .setSchemaType("database2/Document"))
+                        .build();
 
         // Check nothing changed in database2.
-        assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+        expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(expectedTypes);
+    }
+
+    @Test
+    public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+                /*forceOverride=*/ false);
+        mAppSearchImpl.setVisibility("database", Set.of("schema1"));
+
+        // "schema1" is platform hidden now
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .containsExactly("database/schema1");
+
+        // Remove "schema1" by force overriding
+        mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true);
+
+        // Check that "schema1" is no longer considered platform hidden
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .isEmpty();
+
+        // Add "schema1" back, it gets default visibility settings which means it's not platform
+        // hidden.
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .isEmpty();
+    }
+
+    @Test
+    public void testSetVisibility_defaultPlatformVisible() throws Exception {
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .isEmpty();
+    }
+
+    @Test
+    public void testSetVisibility_platformHidden() throws Exception {
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+                /*forceOverride=*/ false);
+        mAppSearchImpl.setVisibility("database", Set.of("Schema"));
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+                .containsExactly("database/Schema");
+    }
+
+    @Test
+    public void testSetVisibility_unknownSchema() throws Exception {
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+                /*forceOverride=*/ false);
+
+        // We'll throw an exception if a client tries to set visibility on a schema we don't know
+        // about.
+        AppSearchException e =
+                expectThrows(
+                        AppSearchException.class,
+                        () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
+        assertThat(e).hasMessageThat().contains("Unknown schema(s)");
+    }
+
+    @Test
+    public void testHasSchemaType() throws Exception {
+        // Nothing exists yet
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse();
+
+        mAppSearchImpl.setSchema(
+                "database",
+                Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue();
+
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "UnknownSchema")).isFalse();
+    }
+
+    @Test
+    public void testGetDatabases() throws Exception {
+        // No client databases exist yet, but the VisibilityStore's does
+        assertThat(mAppSearchImpl.getDatabasesLocked())
+                .containsExactly(VisibilityStore.DATABASE_NAME);
+
+        // Has database1
+        mAppSearchImpl.setSchema(
+                "database1",
+                Collections.singleton(new AppSearchSchema.Builder("schema").build()),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getDatabasesLocked())
+                .containsExactly(VisibilityStore.DATABASE_NAME, "database1");
+
+        // Has both databases
+        mAppSearchImpl.setSchema(
+                "database2",
+                Collections.singleton(new AppSearchSchema.Builder("schema").build()),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getDatabasesLocked())
+                .containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2");
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
new file mode 100644
index 0000000..dfe2de6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.localstorage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class VisibilityStoreTest {
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private AppSearchImpl mAppSearchImpl;
+    private VisibilityStore mVisibilityStore;
+
+    @Before
+    public void setUp() throws Exception {
+        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+        mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
+    }
+
+    @Test
+    public void testSetVisibility() throws Exception {
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema1", "schema2");
+
+        // New .setVisibility() call completely overrides previous visibility settings. So
+        // "schema1" isn't preserved.
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema1", "schema3");
+
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Collections.emptySet());
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
+    }
+
+    @Test
+    public void testRemoveSchemas() throws Exception {
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
+
+        // Removed just schema1
+        mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema2");
+
+        // Removed everything now
+        mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 85d4f01..98392a7 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -32,18 +32,18 @@
 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 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();
+                            "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+                    .setCreationTimestampMillis(12345L)
+                    .build();
     private static final GenericDocument DOCUMENT_PROPERTIES_2 =
             new GenericDocument.Builder<GenericDocument.Builder<?>>(
-                    "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
-            .setCreationTimestampMillis(6789L)
-            .build();
+                            "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+                    .setCreationTimestampMillis(6789L)
+                    .build();
 
     @Test
     public void testDocumentProtoConvert() {
@@ -63,32 +63,42 @@
                         .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");
+        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",
+        propertyProtoMap.put(
+                "longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
+        propertyProtoMap.put(
+                "doubleKey1",
                 PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
-        propertyProtoMap.put("booleanKey1",
+        propertyProtoMap.put(
+                "booleanKey1",
                 PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
-        propertyProtoMap.put("stringKey1",
+        propertyProtoMap.put(
+                "stringKey1",
                 PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
-        propertyProtoMap.put("byteKey1",
-                PropertyProto.newBuilder().setName("byteKey1")
+        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")
+        propertyProtoMap.put(
+                "documentKey1",
+                PropertyProto.newBuilder()
+                        .setName("documentKey1")
                         .addDocumentValues(
                                 GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1)));
-        propertyProtoMap.put("documentKey2",
-                PropertyProto.newBuilder().setName("documentKey2")
+        propertyProtoMap.put(
+                "documentKey2",
+                PropertyProto.newBuilder()
+                        .setName("documentKey2")
                         .addDocumentValues(
                                 GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2)));
         List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
@@ -97,8 +107,7 @@
             documentProtoBuilder.addProperties(propertyProtoMap.get(key));
         }
         DocumentProto documentProto = documentProtoBuilder.build();
-        assertThat(GenericDocumentToProtoConverter.convert(document))
-                .isEqualTo(documentProto);
+        assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto);
         assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index 7336c3c..dedfca4 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -20,9 +20,9 @@
 
 import android.app.appsearch.AppSearchSchema;
 
-import com.android.server.appsearch.proto.IndexingConfig;
 import com.android.server.appsearch.proto.PropertyConfigProto;
 import com.android.server.appsearch.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.proto.StringIndexingConfig;
 import com.android.server.appsearch.proto.TermMatchType;
 
 import org.junit.Test;
@@ -30,84 +30,126 @@
 public class SchemaToProtoConverterTest {
     @Test
     public void testGetProto_Email() {
-        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
-                .addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
-                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("body")
-                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).build();
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .addProperty(
+                                new AppSearchSchema.PropertyConfig.Builder("subject")
+                                        .setDataType(
+                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.PropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.PropertyConfig.Builder("body")
+                                        .setDataType(
+                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.PropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
 
-        SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
-                .setSchemaType("Email")
-                .addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("subject")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("body")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).build();
+        SchemaTypeConfigProto expectedEmailProto =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("Email")
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("subject")
+                                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                        .setStringIndexingConfig(
+                                                StringIndexingConfig.newBuilder()
+                                                        .setTokenizerType(
+                                                                StringIndexingConfig.TokenizerType
+                                                                        .Code.PLAIN)
+                                                        .setTermMatchType(
+                                                                TermMatchType.Code.PREFIX)))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("body")
+                                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                        .setStringIndexingConfig(
+                                                StringIndexingConfig.newBuilder()
+                                                        .setTokenizerType(
+                                                                StringIndexingConfig.TokenizerType
+                                                                        .Code.PLAIN)
+                                                        .setTermMatchType(
+                                                                TermMatchType.Code.PREFIX)))
+                        .build();
 
         assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto);
     }
 
     @Test
     public void testGetProto_MusicRecording() {
-        AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording")
-                .addProperty(new AppSearchSchema.PropertyConfig.Builder("artist")
-                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("pubDate")
-                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
-                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                        .build()
-                ).build();
+        AppSearchSchema musicRecordingSchema =
+                new AppSearchSchema.Builder("MusicRecording")
+                        .addProperty(
+                                new AppSearchSchema.PropertyConfig.Builder("artist")
+                                        .setDataType(
+                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setIndexingType(
+                                                AppSearchSchema.PropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.PropertyConfig.Builder("pubDate")
+                                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                                        .setTokenizerType(
+                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                                        .build())
+                        .build();
 
-        SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
-                .setSchemaType("MusicRecording")
-                .addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("artist")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
-                        .setIndexingConfig(
-                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("pubDate")
-                        .setDataType(PropertyConfigProto.DataType.Code.INT64)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.NONE)
-                                        .setTermMatchType(TermMatchType.Code.UNKNOWN)
-                        )
-                ).build();
+        SchemaTypeConfigProto expectedMusicRecordingProto =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("MusicRecording")
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("artist")
+                                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.REPEATED)
+                                        .setStringIndexingConfig(
+                                                StringIndexingConfig.newBuilder()
+                                                        .setTokenizerType(
+                                                                StringIndexingConfig.TokenizerType
+                                                                        .Code.PLAIN)
+                                                        .setTermMatchType(
+                                                                TermMatchType.Code.PREFIX)))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("pubDate")
+                                        .setDataType(PropertyConfigProto.DataType.Code.INT64)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                        .setStringIndexingConfig(
+                                                StringIndexingConfig.newBuilder()
+                                                        .setTokenizerType(
+                                                                StringIndexingConfig.TokenizerType
+                                                                        .Code.NONE)
+                                                        .setTermMatchType(
+                                                                TermMatchType.Code.UNKNOWN)))
+                        .build();
 
         assertThat(SchemaToProtoConverter.convert(musicRecordingSchema))
                 .isEqualTo(expectedMusicRecordingProto);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 2e9286c..518f532 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -36,9 +36,10 @@
     public void testSingleStringSnippet() {
 
         final String propertyKeyString = "content";
-        final String propertyValueString = "A commonly used fake word is foo.\n"
-                + "   Another nonsense word that’s used a lot\n"
-                + "   is bar.\n";
+        final String propertyValueString =
+                "A commonly used fake word is foo.\n"
+                        + "   Another nonsense word that’s used a lot\n"
+                        + "   is bar.\n";
         final String uri = "uri1";
         final String schemaType = "schema1";
         final String searchWord = "foo";
@@ -46,34 +47,39 @@
         final String window = "is foo";
 
         // Building the SearchResult received from query.
-        PropertyProto property = PropertyProto.newBuilder()
-                .setName(propertyKeyString)
-                .addStringValues(propertyValueString)
-                .build();
-        DocumentProto documentProto = DocumentProto.newBuilder()
-                .setUri(uri)
-                .setSchema(schemaType)
-                .addProperties(property)
-                .build();
-        SnippetProto snippetProto = SnippetProto.newBuilder()
-                .addEntries(SnippetProto.EntryProto.newBuilder()
-                        .setPropertyName(propertyKeyString)
-                        .addSnippetMatches(SnippetMatchProto.newBuilder()
-                                .setValuesIndex(0)
-                                .setExactMatchPosition(29)
-                                .setExactMatchBytes(3)
-                                .setWindowPosition(26)
-                                .setWindowBytes(6)
-                                .build())
-                        .build())
-                .build();
-        SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(documentProto)
-                .setSnippet(snippetProto)
-                .build();
-        SearchResultProto searchResultProto = SearchResultProto.newBuilder()
-                .addResults(resultProto)
-                .build();
+        PropertyProto property =
+                PropertyProto.newBuilder()
+                        .setName(propertyKeyString)
+                        .addStringValues(propertyValueString)
+                        .build();
+        DocumentProto documentProto =
+                DocumentProto.newBuilder()
+                        .setUri(uri)
+                        .setSchema(schemaType)
+                        .addProperties(property)
+                        .build();
+        SnippetProto snippetProto =
+                SnippetProto.newBuilder()
+                        .addEntries(
+                                SnippetProto.EntryProto.newBuilder()
+                                        .setPropertyName(propertyKeyString)
+                                        .addSnippetMatches(
+                                                SnippetMatchProto.newBuilder()
+                                                        .setValuesIndex(0)
+                                                        .setExactMatchPosition(29)
+                                                        .setExactMatchBytes(3)
+                                                        .setWindowPosition(26)
+                                                        .setWindowBytes(6)
+                                                        .build())
+                                        .build())
+                        .build();
+        SearchResultProto.ResultProto resultProto =
+                SearchResultProto.ResultProto.newBuilder()
+                        .setDocument(documentProto)
+                        .setSnippet(snippetProto)
+                        .build();
+        SearchResultProto searchResultProto =
+                SearchResultProto.newBuilder().addResults(resultProto).build();
 
         // Making ResultReader and getting Snippet values.
         SearchResultPage searchResultPage =
@@ -83,11 +89,11 @@
             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.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.getSnippetPosition())
+                    .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
             assertThat(match.getSnippet()).isEqualTo(window);
         }
     }
@@ -97,9 +103,10 @@
     public void testNoSnippets() throws Exception {
 
         final String propertyKeyString = "content";
-        final String propertyValueString = "A commonly used fake word is foo.\n"
-                + "   Another nonsense word that’s used a lot\n"
-                + "   is bar.\n";
+        final String propertyValueString =
+                "A commonly used fake word is foo.\n"
+                        + "   Another nonsense word that’s used a lot\n"
+                        + "   is bar.\n";
         final String uri = "uri1";
         final String schemaType = "schema1";
         final String searchWord = "foo";
@@ -107,21 +114,21 @@
         final String window = "is foo";
 
         // Building the SearchResult received from query.
-        PropertyProto property = PropertyProto.newBuilder()
-                .setName(propertyKeyString)
-                .addStringValues(propertyValueString)
-                .build();
-        DocumentProto documentProto = DocumentProto.newBuilder()
-                .setUri(uri)
-                .setSchema(schemaType)
-                .addProperties(property)
-                .build();
-        SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(documentProto)
-                .build();
-        SearchResultProto searchResultProto = SearchResultProto.newBuilder()
-                .addResults(resultProto)
-                .build();
+        PropertyProto property =
+                PropertyProto.newBuilder()
+                        .setName(propertyKeyString)
+                        .addStringValues(propertyValueString)
+                        .build();
+        DocumentProto documentProto =
+                DocumentProto.newBuilder()
+                        .setUri(uri)
+                        .setSchema(schemaType)
+                        .addProperties(property)
+                        .build();
+        SearchResultProto.ResultProto resultProto =
+                SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build();
+        SearchResultProto searchResultProto =
+                SearchResultProto.newBuilder().addResults(resultProto).build();
 
         SearchResultPage searchResultPage =
                 SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto);
@@ -135,54 +142,57 @@
         final String searchWord = "Test";
 
         // Building the SearchResult received from query.
-        PropertyProto property1 = PropertyProto.newBuilder()
-                .setName("sender.name")
-                .addStringValues("Test Name Jr.")
-                .build();
-        PropertyProto property2 = PropertyProto.newBuilder()
-                .setName("sender.email")
-                .addStringValues("TestNameJr@gmail.com")
-                .build();
-        DocumentProto documentProto = DocumentProto.newBuilder()
-                .setUri("uri1")
-                .setSchema("schema1")
-                .addProperties(property1)
-                .addProperties(property2)
-                .build();
-        SnippetProto snippetProto = SnippetProto.newBuilder()
-                .addEntries(
-                        SnippetProto.EntryProto.newBuilder()
-                                .setPropertyName("sender.name")
-                                .addSnippetMatches(
-                                        SnippetMatchProto.newBuilder()
-                                                .setValuesIndex(0)
-                                                .setExactMatchPosition(0)
-                                                .setExactMatchBytes(4)
-                                                .setWindowPosition(0)
-                                                .setWindowBytes(9)
-                                                .build())
-                                .build())
-                .addEntries(
-                        SnippetProto.EntryProto.newBuilder()
-                                .setPropertyName("sender.email")
-                                .addSnippetMatches(
-                                        SnippetMatchProto.newBuilder()
-                                                .setValuesIndex(0)
-                                                .setExactMatchPosition(0)
-                                                .setExactMatchBytes(20)
-                                                .setWindowPosition(0)
-                                                .setWindowBytes(20)
-                                                .build())
-                                .build()
-                )
-                .build();
-        SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(documentProto)
-                .setSnippet(snippetProto)
-                .build();
-        SearchResultProto searchResultProto = SearchResultProto.newBuilder()
-                .addResults(resultProto)
-                .build();
+        PropertyProto property1 =
+                PropertyProto.newBuilder()
+                        .setName("sender.name")
+                        .addStringValues("Test Name Jr.")
+                        .build();
+        PropertyProto property2 =
+                PropertyProto.newBuilder()
+                        .setName("sender.email")
+                        .addStringValues("TestNameJr@gmail.com")
+                        .build();
+        DocumentProto documentProto =
+                DocumentProto.newBuilder()
+                        .setUri("uri1")
+                        .setSchema("schema1")
+                        .addProperties(property1)
+                        .addProperties(property2)
+                        .build();
+        SnippetProto snippetProto =
+                SnippetProto.newBuilder()
+                        .addEntries(
+                                SnippetProto.EntryProto.newBuilder()
+                                        .setPropertyName("sender.name")
+                                        .addSnippetMatches(
+                                                SnippetMatchProto.newBuilder()
+                                                        .setValuesIndex(0)
+                                                        .setExactMatchPosition(0)
+                                                        .setExactMatchBytes(4)
+                                                        .setWindowPosition(0)
+                                                        .setWindowBytes(9)
+                                                        .build())
+                                        .build())
+                        .addEntries(
+                                SnippetProto.EntryProto.newBuilder()
+                                        .setPropertyName("sender.email")
+                                        .addSnippetMatches(
+                                                SnippetMatchProto.newBuilder()
+                                                        .setValuesIndex(0)
+                                                        .setExactMatchPosition(0)
+                                                        .setExactMatchBytes(20)
+                                                        .setWindowPosition(0)
+                                                        .setWindowBytes(20)
+                                                        .build())
+                                        .build())
+                        .build();
+        SearchResultProto.ResultProto resultProto =
+                SearchResultProto.ResultProto.newBuilder()
+                        .setDocument(documentProto)
+                        .setSnippet(snippetProto)
+                        .build();
+        SearchResultProto searchResultProto =
+                SearchResultProto.newBuilder().addResults(resultProto).build();
 
         // Making ResultReader and getting Snippet values.
         SearchResultPage searchResultPage =
@@ -192,21 +202,21 @@
             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.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.getSnippetPosition())
+                    .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
             assertThat(match1.getSnippet()).isEqualTo("Test Name");
 
             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.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.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/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
new file mode 100644
index 0000000..efdbda3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.face.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class FaceProviderTest {
+
+    private static final String TAG = "FaceProviderTest";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+
+    private SensorProps[] mSensorProps;
+    private LockoutResetDispatcher mLockoutResetDispatcher;
+    private FaceProvider mFaceProvider;
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+
+        final SensorProps sensor1 = new SensorProps();
+        sensor1.commonProps = new CommonProps();
+        sensor1.commonProps.sensorId = 0;
+        final SensorProps sensor2 = new SensorProps();
+        sensor2.commonProps = new CommonProps();
+        sensor2.commonProps.sensorId = 1;
+
+        mSensorProps = new SensorProps[] {sensor1, sensor2};
+
+        mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+
+        mFaceProvider = new FaceProvider(mContext, mSensorProps, TAG,
+                mLockoutResetDispatcher);
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Test
+    public void halServiceDied_resetsAllSchedulers() {
+        assertEquals(mSensorProps.length, mFaceProvider.getSensorProperties().size());
+
+        // Schedule N operations on each sensor
+        final int numFakeOperations = 10;
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            for (int i = 0; i < numFakeOperations; i++) {
+                final ClientMonitor testMonitor = mock(ClientMonitor.class);
+                when(testMonitor.getFreshDaemon()).thenReturn(new Object());
+                scheduler.scheduleClientMonitor(testMonitor);
+            }
+        }
+
+        waitForIdle();
+        // The right amount of pending and current operations are scheduled
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
+            assertNotNull(scheduler.getCurrentClient());
+        }
+
+        // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+        // serviceDied directly.
+        mFaceProvider.binderDied();
+        waitForIdle();
+
+        // No pending operations, no current operation.
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            assertNull(scheduler.getCurrentClient());
+            assertEquals(0, scheduler.getCurrentPendingCount());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
similarity index 81%
rename from services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java
rename to services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 35fc7f0..99aab5c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.face;
+package com.android.server.biometrics.sensors.face.hidl;
 
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -28,8 +29,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.face.hidl.Face10;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,6 +50,8 @@
     private Context mContext;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private BiometricScheduler mScheduler;
 
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
@@ -68,7 +71,7 @@
         mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
         mFace10 = new Face10(mContext, SENSOR_ID, BiometricManager.Authenticators.BIOMETRIC_STRONG,
                 mLockoutResetDispatcher, false /* supportsSelfIllumination */,
-                1 /* maxTemplatesAllowed */);
+                1 /* maxTemplatesAllowed */, mScheduler);
         mBinder = new Binder();
     }
 
@@ -78,4 +81,13 @@
                 0 /* challenge */);
         waitForIdle();
     }
+
+    @Test
+    public void halServiceDied_resetsScheduler() {
+        // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+        // serviceDied directly.
+        mFace10.serviceDied(0 /* cookie */);
+        waitForIdle();
+        verify(mScheduler).reset();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
new file mode 100644
index 0000000..624775b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class FingerprintProviderTest {
+
+    private static final String TAG = "FingerprintProviderTest";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+
+    private SensorProps[] mSensorProps;
+    private LockoutResetDispatcher mLockoutResetDispatcher;
+    private FingerprintProvider mFingerprintProvider;
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+
+        final SensorProps sensor1 = new SensorProps();
+        sensor1.commonProps = new CommonProps();
+        sensor1.commonProps.sensorId = 0;
+        final SensorProps sensor2 = new SensorProps();
+        sensor2.commonProps = new CommonProps();
+        sensor2.commonProps.sensorId = 1;
+
+        mSensorProps = new SensorProps[] {sensor1, sensor2};
+
+        mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+
+        mFingerprintProvider = new FingerprintProvider(mContext, mSensorProps, TAG,
+                mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Test
+    public void halServiceDied_resetsAllSchedulers() {
+        assertEquals(mSensorProps.length, mFingerprintProvider.getSensorProperties().size());
+
+        // Schedule N operations on each sensor
+        final int numFakeOperations = 10;
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            for (int i = 0; i < numFakeOperations; i++) {
+                final ClientMonitor testMonitor = mock(ClientMonitor.class);
+                when(testMonitor.getFreshDaemon()).thenReturn(new Object());
+                scheduler.scheduleClientMonitor(testMonitor);
+            }
+        }
+
+        waitForIdle();
+        // The right amount of pending and current operations are scheduled
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
+            assertNotNull(scheduler.getCurrentClient());
+        }
+
+        // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+        // serviceDied directly.
+        mFingerprintProvider.binderDied();
+        waitForIdle();
+
+        // No pending operations, no current operation.
+        for (SensorProps prop : mSensorProps) {
+            final BiometricScheduler scheduler =
+                    mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+            assertNull(scheduler.getCurrentClient());
+            assertEquals(0, scheduler.getCurrentPendingCount());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
new file mode 100644
index 0000000..b2aeb33
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -0,0 +1,95 @@
+/*
+ * 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.hidl;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class Fingerprint21Test {
+
+    private static final String TAG = "Fingerprint21Test";
+    private static final int SENSOR_ID = 1;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    Fingerprint21.HalResultController mHalResultController;
+    @Mock
+    private BiometricScheduler mScheduler;
+
+    private LockoutResetDispatcher mLockoutResetDispatcher;
+    private Fingerprint21 mFingerprint21;
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser)))
+                .thenReturn(5);
+
+        mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+        mFingerprint21 = new Fingerprint21(mContext, mScheduler,
+                new Handler(Looper.getMainLooper()), SENSOR_ID,
+                BiometricManager.Authenticators.BIOMETRIC_WEAK, mLockoutResetDispatcher,
+                mHalResultController);
+    }
+
+    @Test
+    public void halServiceDied_resetsScheduler() {
+        // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+        // serviceDied directly.
+        mFingerprint21.serviceDied(0 /* cookie */);
+        waitForIdle();
+        verify(mScheduler).reset();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index cbe49eb..904e93b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -36,7 +36,6 @@
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.permission.IPermissionManager;
 import android.security.KeyChain;
 import android.telephony.TelephonyManager;
@@ -51,6 +50,7 @@
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index cb49a51..1d2dcae 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -31,7 +31,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.test.mock.MockContext;
 import android.util.ArrayMap;
 import android.util.ExceptionUtils;
@@ -39,6 +38,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.util.FunctionalUtils;
+import com.android.server.pm.UserManagerInternal;
 
 import org.junit.Assert;
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 431cc27..34313b8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -54,7 +54,6 @@
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.permission.IPermissionManager;
 import android.provider.Settings;
 import android.security.KeyChain;
@@ -70,6 +69,7 @@
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.File;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index cdb49ad..dfeed13 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -18,6 +18,7 @@
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_INVALID;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
@@ -35,6 +36,7 @@
 import android.os.PowerManager;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
+import android.provider.Settings.Global;
 import android.sysprop.HdmiProperties;
 import android.view.KeyEvent;
 
@@ -93,6 +95,12 @@
                     @Override
                     void standby() {
                         mStandby = true;
+                        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+                    }
+
+                    @Override
+                    boolean isStandbyMessageReceived() {
+                        return mStandby;
                     }
 
                     @Override
@@ -126,6 +134,7 @@
                     }
                 };
 
+        mHdmiControlService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
         mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
         mHdmiCecLocalDevicePlayback.init();
         mHdmiControlService.setIoLooper(mMyLooper);
@@ -813,6 +822,46 @@
     }
 
     @Test
+    public void losingActiveSource_standbyNow_verifyStandbyMessageIsSentOnNextStandby() {
+        // As described in b/161097846.
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        mStandby = false;
+        // 1. DUT is <AS>.
+        HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                                         mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message1)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+        assertThat(mStandby).isFalse();
+        // 2. DUT loses <AS> and goes to sleep.
+        HdmiCecMessage message2 = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message2)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
+        assertThat(mStandby).isTrue();
+        // 3. DUT becomes <AS> again.
+        mWokenUp = false;
+        HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                mPlaybackPhysicalAddress);
+        mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
+        mTestLooper.dispatchAll();
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+                mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mWokenUp).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+        // 4. DUT turned off.
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+        HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
+    }
+
+    @Test
     public void sendVolumeKeyEvent_up_volumeEnabled() {
         mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
@@ -1199,4 +1248,91 @@
         assertThat(mHdmiControlService.getActiveSource().getPhysicalAddress()).isEqualTo(
                 externalDevice.getPhysicalAddress());
     }
+
+    @Test
+    public void queryDisplayStatus() {
+        mHdmiControlService.queryDisplayStatus(new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+            }
+        });
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+                mPlaybackLogicalAddress, Constants.ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void toggleAndFollowTvPower_ToTv_TvStatusOn() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+        mStandby = false;
+        mHdmiControlService.toggleAndFollowTvPower();
+        HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+                mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
+        assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
+                mPlaybackLogicalAddress, ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mStandby).isTrue();
+    }
+
+    @Test
+    public void toggleAndFollowTvPower_Broadcast_TvStatusOn() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        mStandby = false;
+        mHdmiControlService.toggleAndFollowTvPower();
+        HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+                mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
+        assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
+                mPlaybackLogicalAddress, ADDR_BROADCAST);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mStandby).isTrue();
+    }
+
+    @Test
+    public void toggleAndFollowTvPower_TvStatusStandby() {
+        mStandby = false;
+        mHdmiControlService.toggleAndFollowTvPower();
+        HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+                mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+        assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+                ADDR_TV);
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void toggleAndFollowTvPower_TvStatusUnknown() {
+        mStandby = false;
+        mHdmiControlService.toggleAndFollowTvPower();
+        HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+                mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_UNKNOWN);
+        assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, Constants.ADDR_TV,
+                HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+        HdmiCecMessage userControlReleased = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, Constants.ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed);
+        assertThat(mNativeWrapper.getResultMessages()).contains(userControlReleased);
+        assertThat(mStandby).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 6f62014..6496264 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -22,6 +22,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.platform.test.annotations.Presubmit;
 
@@ -103,7 +104,7 @@
     @Test
     public void buildReportFeatures_basicTv_1_4() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_1_4,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
 
@@ -113,7 +114,7 @@
     @Test
     public void buildReportFeatures_basicPlayback_1_4() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                Constants.VERSION_1_4,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
 
@@ -123,7 +124,7 @@
     @Test
     public void buildReportFeatures_basicPlaybackAudioSystem_1_4() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                Constants.VERSION_1_4,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
                         HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
@@ -134,7 +135,7 @@
     @Test
     public void buildReportFeatures_basicTv_2_0() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_2_0,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
 
@@ -144,7 +145,7 @@
     @Test
     public void buildReportFeatures_remoteControlTv_2_0() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_2_0,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList());
 
@@ -154,7 +155,7 @@
     @Test
     public void buildReportFeatures_remoteControlPlayback_2_0() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_2_0,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
                 Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                         Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList());
@@ -165,7 +166,7 @@
     @Test
     public void buildReportFeatures_deviceFeaturesTv_2_0() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_2_0,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
                 Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
                 Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN));
@@ -176,7 +177,7 @@
     @Test
     public void buildReportFeatures_deviceFeaturesPlayback_2_0() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                Constants.VERSION_2_0,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
                 Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                         Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index f3a4366..a05cbb4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -67,8 +67,8 @@
     public void isValid_reportPowerStatus() {
         assertMessageValidity("04:90:00").isEqualTo(OK);
         assertMessageValidity("04:90:03:05").isEqualTo(OK);
+        assertMessageValidity("0F:90:00").isEqualTo(OK);
 
-        assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION);
         assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE);
         assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT);
         assertMessageValidity("04:90:04").isEqualTo(ERROR_PARAMETER);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
new file mode 100644
index 0000000..3cc7c6b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.server.hdmi;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecPowerStatusController} class. */
+public class HdmiCecPowerStatusControllerTest {
+
+    public static final int[] ARRAY_POWER_STATUS = new int[]{HdmiControlManager.POWER_STATUS_ON,
+            HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
+            HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
+            HdmiControlManager.POWER_STATUS_STANDBY};
+    private HdmiCecPowerStatusController mHdmiCecPowerStatusController;
+    private FakeNativeWrapper mNativeWrapper;
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_b;
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private IThermalService mIThermalServiceMock;
+    private HdmiControlService mHdmiControlService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+        Looper myLooper = mTestLooper.getLooper();
+        PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper));
+        when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+        when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+        HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(contextSpy);
+
+        mHdmiControlService = new HdmiControlService(contextSpy) {
+            @Override
+            boolean isControlEnabled() {
+                return true;
+            }
+
+            @Override
+            boolean isPlaybackDevice() {
+                return true;
+            }
+
+            @Override
+            void writeStringSystemProperty(String key, String value) {
+                // do nothing
+            }
+
+            @Override
+            int getCecVersion() {
+                return mHdmiCecVersion;
+            }
+
+            @Override
+            boolean isPowerStandby() {
+                return false;
+            }
+
+            @Override
+            HdmiCecConfig getHdmiCecConfig() {
+                return hdmiCecConfig;
+            }
+        };
+        mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        HdmiCecLocalDevicePlayback hdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(
+                mHdmiControlService);
+        hdmiCecLocalDevicePlayback.init();
+        mHdmiControlService.setIoLooper(myLooper);
+        mNativeWrapper = new FakeNativeWrapper();
+        HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+        mHdmiControlService.setCecController(hdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mLocalDevices.add(hdmiCecLocalDevicePlayback);
+        HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+        hdmiPortInfos[0] =
+                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+        mNativeWrapper.setPortInfo(hdmiPortInfos);
+        mNativeWrapper.setPortConnectionStatus(1, true);
+        mHdmiControlService.initService();
+        mHdmiControlService.getHdmiCecNetwork().initPortInfo();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        mTestLooper.dispatchAll();
+
+        mHdmiCecPowerStatusController = new HdmiCecPowerStatusController(mHdmiControlService);
+        mNativeWrapper.clearResultMessages();
+    }
+
+    @Test
+    public void setPowerStatus() {
+        for (int status : ARRAY_POWER_STATUS) {
+            mHdmiCecPowerStatusController.setPowerStatus(status);
+            assertThat(mHdmiCecPowerStatusController.getPowerStatus()).isEqualTo(status);
+        }
+    }
+
+    @Test
+    public void isPowerStatusOn() {
+        for (int status : ARRAY_POWER_STATUS) {
+            mHdmiCecPowerStatusController.setPowerStatus(status);
+            assertThat(mHdmiCecPowerStatusController.isPowerStatusOn()).isEqualTo(
+                    HdmiControlManager.POWER_STATUS_ON == status);
+        }
+    }
+
+    @Test
+    public void isPowerStatusStandby() {
+        for (int status : ARRAY_POWER_STATUS) {
+            mHdmiCecPowerStatusController.setPowerStatus(status);
+            assertThat(mHdmiCecPowerStatusController.isPowerStatusStandby()).isEqualTo(
+                    HdmiControlManager.POWER_STATUS_STANDBY == status);
+        }
+    }
+
+    @Test
+    public void isPowerStatusTransientToOn() {
+        for (int status : ARRAY_POWER_STATUS) {
+            mHdmiCecPowerStatusController.setPowerStatus(status);
+            assertThat(mHdmiCecPowerStatusController.isPowerStatusTransientToOn()).isEqualTo(
+                    HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON == status);
+        }
+    }
+
+    @Test
+    public void isPowerStatusTransientToStandby() {
+        for (int status : ARRAY_POWER_STATUS) {
+            mHdmiCecPowerStatusController.setPowerStatus(status);
+            assertThat(mHdmiCecPowerStatusController.isPowerStatusTransientToStandby()).isEqualTo(
+                    HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY == status);
+        }
+    }
+
+    @Test
+    public void setPowerStatus_doesntSendBroadcast_1_4() {
+        mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_ON);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
+    }
+
+    @Test
+    public void setPowerStatus_transient_doesntSendBroadcast_1_4() {
+        mHdmiCecPowerStatusController.setPowerStatus(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
+    }
+
+    @Test
+    public void setPowerStatus_fast_transient_doesntSendBroadcast_1_4() {
+        mHdmiCecPowerStatusController.setPowerStatus(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
+    }
+
+    @Test
+    public void setPowerStatus_sendsBroadcast_2_0() {
+        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+
+        mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_ON);
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
+    }
+
+    @Test
+    public void setPowerStatus_transient_sendsBroadcast_2_0() {
+        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+
+        mHdmiCecPowerStatusController.setPowerStatus(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
+    }
+
+    @Test
+    public void setPowerStatus_fast_transient_doesntSendBroadcast_2_0() {
+        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+
+        mHdmiCecPowerStatusController.setPowerStatus(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 2e4bed9..9d767cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -243,6 +243,50 @@
     }
 
     @Test
+    public void initialPowerStatus_normalBoot_goToStandby_doesNotBroadcastsPowerStatus_1_4() {
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_b);
+
+        mHdmiControlService.setControlEnabled(true);
+        mNativeWrapper.clearResultMessages();
+
+        assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_STANDBY);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
+    }
+
+    @Test
+    public void initialPowerStatus_normalBoot_goToStandby_broadcastsPowerStatus_2_0() {
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0);
+
+        mHdmiControlService.setControlEnabled(true);
+        mNativeWrapper.clearResultMessages();
+
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+                HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+                Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+                HdmiControlManager.POWER_STATUS_STANDBY);
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
+    }
+
+    @Test
     public void setAndGetCecVolumeControlEnabled_isApi() {
         mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
         assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
@@ -470,7 +514,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
-                Constants.ADDR_PLAYBACK_1, Constants.VERSION_2_0,
+                Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
                 mMyPlaybackDevice.getDeviceFeatures());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index d44d37e..679d690 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -44,7 +44,6 @@
 import android.os.IProgressListener;
 import android.os.RemoteException;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.security.KeyStore;
@@ -57,6 +56,7 @@
 import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 4e1454b..73191dc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -27,7 +27,6 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.security.KeyStore;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -35,6 +34,7 @@
 import com.android.internal.widget.LockscreenCredential;
 import com.android.server.ServiceThread;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.FileNotFoundException;
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index c4d185c..e46ab6b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -84,7 +84,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
 import android.util.ArrayMap;
@@ -587,7 +586,7 @@
 
         @Override
         boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
-            return true;
+            return mInjectCheckAccessShortcutsPermission;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 95c881e..4b90a5c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -47,7 +47,6 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 7694d09..90c2982 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -21,7 +21,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import static java.lang.Boolean.TRUE;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import android.annotation.NonNull;
@@ -32,6 +34,7 @@
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.ServiceInfo;
@@ -81,7 +84,9 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 @Presubmit
@@ -99,6 +104,8 @@
     private static final String TEST_APP1_APK = "PackageParserTestApp1.apk";
     private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
     private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
+    private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+    private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
 
     @Before
     public void setUp() throws IOException {
@@ -270,6 +277,234 @@
         }
     }
 
+    private static final int PROPERTY_TYPE_BOOLEAN = 1;
+    private static final int PROPERTY_TYPE_FLOAT = 2;
+    private static final int PROPERTY_TYPE_INTEGER = 3;
+    private static final int PROPERTY_TYPE_RESOURCE = 4;
+    private static final int PROPERTY_TYPE_STRING = 5;
+    public void assertProperty(Map<String, Property> properties, String propertyName,
+            int propertyType, Object propertyValue) {
+        assertTrue(properties.containsKey(propertyName));
+
+        final Property testProperty = properties.get(propertyName);
+        assertEquals(propertyType, testProperty.getType());
+
+        if (propertyType == PROPERTY_TYPE_BOOLEAN) {
+            assertTrue(testProperty.isBoolean());
+            assertFalse(testProperty.isFloat());
+            assertFalse(testProperty.isInteger());
+            assertFalse(testProperty.isResourceId());
+            assertFalse(testProperty.isString());
+
+            // assert the property's type is set correctly
+            final Boolean boolValue = (Boolean) propertyValue;
+            if (boolValue.booleanValue()) {
+                assertTrue(testProperty.getBoolean());
+            } else {
+                assertFalse(testProperty.getBoolean());
+            }
+            // assert the other values have an appropriate default
+            assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+            assertEquals(0, testProperty.getInteger());
+            assertEquals(0, testProperty.getResourceId());
+            assertEquals(null, testProperty.getString());
+        } else if (propertyType == PROPERTY_TYPE_FLOAT) {
+            assertFalse(testProperty.isBoolean());
+            assertTrue(testProperty.isFloat());
+            assertFalse(testProperty.isInteger());
+            assertFalse(testProperty.isResourceId());
+            assertFalse(testProperty.isString());
+
+            // assert the property's type is set correctly
+            final Float floatValue = (Float) propertyValue;
+            assertEquals(floatValue.floatValue(), testProperty.getFloat(), 0.0f);
+            // assert the other values have an appropriate default
+            assertFalse(testProperty.getBoolean());
+            assertEquals(0, testProperty.getInteger());
+            assertEquals(0, testProperty.getResourceId());
+            assertEquals(null, testProperty.getString());
+        } else if (propertyType == PROPERTY_TYPE_INTEGER) {
+            assertFalse(testProperty.isBoolean());
+            assertFalse(testProperty.isFloat());
+            assertTrue(testProperty.isInteger());
+            assertFalse(testProperty.isResourceId());
+            assertFalse(testProperty.isString());
+
+            // assert the property's type is set correctly
+            final Integer integerValue = (Integer) propertyValue;
+            assertEquals(integerValue.intValue(), testProperty.getInteger());
+            // assert the other values have an appropriate default
+            assertFalse(testProperty.getBoolean());
+            assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+            assertEquals(0, testProperty.getResourceId());
+            assertEquals(null, testProperty.getString());
+        } else if (propertyType == PROPERTY_TYPE_RESOURCE) {
+            assertFalse(testProperty.isBoolean());
+            assertFalse(testProperty.isFloat());
+            assertFalse(testProperty.isInteger());
+            assertTrue(testProperty.isResourceId());
+            assertFalse(testProperty.isString());
+
+            // assert the property's type is set correctly
+            final Integer resourceValue = (Integer) propertyValue;
+            assertEquals(resourceValue.intValue(), testProperty.getResourceId());
+            // assert the other values have an appropriate default
+            assertFalse(testProperty.getBoolean());
+            assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+            assertEquals(0, testProperty.getInteger());
+            assertEquals(null, testProperty.getString());
+        } else if (propertyType == PROPERTY_TYPE_STRING) {
+            assertFalse(testProperty.isBoolean());
+            assertFalse(testProperty.isFloat());
+            assertFalse(testProperty.isInteger());
+            assertFalse(testProperty.isResourceId());
+            assertTrue(testProperty.isString());
+
+            // assert the property's type is set correctly
+            final String stringValue = (String) propertyValue;
+            assertEquals(stringValue, testProperty.getString());
+            // assert the other values have an appropriate default
+            assertFalse(testProperty.getBoolean());
+            assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+            assertEquals(0, testProperty.getInteger());
+            assertEquals(0, testProperty.getResourceId());
+        } else {
+            fail("Unknown property type");
+        }
+    }
+
+    @Test
+    public void testParseApplicationProperties() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final Map<String, Property> properties = pkg.getProperties();
+            assertEquals(10, properties.size());
+            assertProperty(properties,
+                    "android.cts.PROPERTY_RESOURCE_XML", PROPERTY_TYPE_RESOURCE, 0x7f060000);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_RESOURCE_INTEGER", PROPERTY_TYPE_RESOURCE, 0x7f040000);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_BOOLEAN", PROPERTY_TYPE_BOOLEAN, TRUE);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE", PROPERTY_TYPE_BOOLEAN, TRUE);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_FLOAT", PROPERTY_TYPE_FLOAT, 3.14f);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_FLOAT_VIA_RESOURCE", PROPERTY_TYPE_FLOAT, 2.718f);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_INTEGER", PROPERTY_TYPE_INTEGER, 42);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_INTEGER_VIA_RESOURCE", PROPERTY_TYPE_INTEGER, 123);
+            assertProperty(properties,
+                    "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala");
+            assertProperty(properties,
+                    "android.cts.PROPERTY_STRING_VIA_RESOURCE", PROPERTY_TYPE_STRING, "giraffe");
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
+    public void testParseActivityProperties() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedActivity> activities = pkg.getActivities();
+            for (ParsedActivity activity : activities) {
+                final Map<String, Property> properties = activity.getProperties();
+                if ((PACKAGE_NAME + ".MyActivityAlias").equals(activity.getName())) {
+                    assertEquals(2, properties.size());
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_ACTIVITY_ALIAS", PROPERTY_TYPE_INTEGER, 123);
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123);
+                } else if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
+                    assertEquals(3, properties.size());
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_ACTIVITY", PROPERTY_TYPE_INTEGER, 123);
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123);
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala activity");
+                } else if ("android.app.AppDetailsActivity".equals(activity.getName())) {
+                    // ignore default added activity
+                } else {
+                    fail("Found unknown activity; name = " + activity.getName());
+                }
+            }
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
+    public void testParseProviderProperties() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedProvider> providers = pkg.getProviders();
+            for (ParsedProvider provider : providers) {
+                final Map<String, Property> properties = provider.getProperties();
+                if ((PACKAGE_NAME + ".MyProvider").equals(provider.getName())) {
+                    assertEquals(1, properties.size());
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_PROVIDER", PROPERTY_TYPE_INTEGER, 123);
+                } else {
+                    fail("Found unknown provider; name = " + provider.getName());
+                }
+            }
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
+    public void testParseReceiverProperties() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedActivity> receivers = pkg.getReceivers();
+            for (ParsedActivity receiver : receivers) {
+                final Map<String, Property> properties = receiver.getProperties();
+                if ((PACKAGE_NAME + ".MyReceiver").equals(receiver.getName())) {
+                    assertEquals(2, properties.size());
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_RECEIVER", PROPERTY_TYPE_INTEGER, 123);
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala receiver");
+                } else {
+                    fail("Found unknown receiver; name = " + receiver.getName());
+                }
+            }
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
+    public void testParseServiceProperties() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedService> services = pkg.getServices();
+            for (ParsedService service : services) {
+                final Map<String, Property> properties = service.getProperties();
+                if ((PACKAGE_NAME + ".MyService").equals(service.getName())) {
+                    assertEquals(2, properties.size());
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_SERVICE", PROPERTY_TYPE_INTEGER, 123);
+                    assertProperty(properties,
+                            "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_RESOURCE, 0x7f040000);
+                } else {
+                    fail("Found unknown service; name = " + service.getName());
+                }
+            }
+        } finally {
+            testFile.delete();
+        }
+    }
+
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
@@ -386,6 +621,13 @@
                     b.getInstrumentations().get(i));
         }
 
+        assertEquals(a.getProperties().size(), b.getProperties().size());
+        final Iterator<String> iter = a.getProperties().keySet().iterator();
+        while (iter.hasNext()) {
+            final String key = iter.next();
+            assertEquals(a.getProperties().get(key), b.getProperties().get(key));
+        }
+
         assertEquals(a.getRequestedPermissions(), b.getRequestedPermissions());
         assertEquals(a.getProtectedBroadcasts(), b.getProtectedBroadcasts());
         assertEquals(a.getLibraryNames(), b.getLibraryNames());
@@ -443,6 +685,13 @@
             assertEquals(aIntent.getNonLocalizedLabel(), bIntent.getNonLocalizedLabel());
             assertEquals(aIntent.getIcon(), bIntent.getIcon());
         }
+
+        assertEquals(a.getProperties().size(), b.getProperties().size());
+        final Iterator<String> iter = a.getProperties().keySet().iterator();
+        while (iter.hasNext()) {
+            final String key = iter.next();
+            assertEquals(a.getProperties().get(key), b.getProperties().get(key));
+        }
     }
 
     private static void assertPermissionsEqual(ParsedPermission a, ParsedPermission b) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 1ca3c74..fcbb5ed 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -46,7 +46,6 @@
 import android.content.res.TypedArray;
 import android.os.Environment;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c7a05ba..194ae05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -490,6 +490,7 @@
         mManager.pushDynamicShortcut(s8);
         assertEquals(4, getCallerShortcut("s8").getRank());
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s8"), HANDLE_USER_0,
                     CACHE_OWNER_0);
         });
@@ -1456,6 +1457,7 @@
 
         // Cache 1 and 2
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"),
                     HANDLE_USER_0, CACHE_OWNER_0);
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"),
@@ -1538,6 +1540,7 @@
 
         // Cache some, but non long lived shortcuts will be ignored.
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"),
                     HANDLE_USER_0, CACHE_OWNER_0);
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
@@ -1597,6 +1600,48 @@
                 "s2");
     }
 
+    public void testCachedShortcuts_accessShortcutsPermission() {
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+                    makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
+                    makeLongLivedShortcut("s4"))));
+        });
+
+        // s1 is not long lived and will be ignored.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = false;
+            assertExpectException(
+                    SecurityException.class, "Caller can't access shortcut information", () -> {
+                        mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"),
+                                HANDLE_USER_0, CACHE_OWNER_0);
+                    });
+            // Give ACCESS_SHORTCUTS permission to LAUNCHER_1
+            mInjectCheckAccessShortcutsPermission = true;
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"),
+                    HANDLE_USER_0, CACHE_OWNER_0);
+        });
+
+        setCaller(CALLING_PACKAGE_1);
+
+        // Get cached shortcuts
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s2", "s3");
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = false;
+            assertExpectException(
+                    SecurityException.class, "Caller can't access shortcut information", () -> {
+                        mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
+                                HANDLE_USER_0, CACHE_OWNER_0);
+                    });
+            // Give ACCESS_SHORTCUTS permission to LAUNCHER_1
+            mInjectCheckAccessShortcutsPermission = true;
+            mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
+                    HANDLE_USER_0, CACHE_OWNER_0);
+        });
+
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
+    }
+
     public void testCachedShortcuts_canPassShortcutLimit() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -1609,6 +1654,7 @@
 
         // Cache All
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
                     HANDLE_USER_0, CACHE_OWNER_0);
         });
@@ -1808,6 +1854,7 @@
         setCaller(LAUNCHER_1);
 
         // Cache some shortcuts. Only long lived shortcuts can get cached.
+        mInjectCheckAccessShortcutsPermission = true;
         mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser(),
                 CACHE_OWNER_0);
         mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser(),
@@ -2009,6 +2056,53 @@
         });
     }
 
+    public void testGetShortcuts_personsFlag() {
+        ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
+                .setShortLabel("label")
+                .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+                .setPerson(makePerson("person", "personKey", "personUri"))
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .build();
+
+        setCaller(CALLING_PACKAGE_1);
+        assertTrue(mManager.setDynamicShortcuts(list(s)));
+
+        setCaller(LAUNCHER_1);
+
+        assertNull(mLauncherApps.getShortcuts(buildQuery(
+                /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+                ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                getCallingUser()).get(0).getPersons());
+
+        assertNull(mLauncherApps.getShortcuts(buildQuery(
+                /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+                ShortcutQuery.FLAG_MATCH_DYNAMIC),
+                getCallingUser()).get(0).getPersons());
+
+        // Using FLAG_GET_PERSONS_DATA should fail without permission
+        mInjectCheckAccessShortcutsPermission = false;
+        assertExpectException(
+                SecurityException.class, "Caller can't access shortcut information", () -> {
+                    mLauncherApps.getShortcuts(buildQuery(
+                            /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+                            ShortcutQuery.FLAG_MATCH_DYNAMIC
+                                    | ShortcutQuery.FLAG_GET_PERSONS_DATA),
+                            getCallingUser());
+                });
+
+        mInjectCheckAccessShortcutsPermission = true;
+        assertEquals("person", mLauncherApps.getShortcuts(buildQuery(
+                /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+                ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA),
+                getCallingUser()).get(0).getPersons()[0].getName());
+
+        assertNull(mLauncherApps.getShortcuts(buildQuery(
+                /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+                ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA
+                        | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                getCallingUser()).get(0).getPersons());
+    }
+
     // TODO resource
     public void testGetShortcutInfo() {
         // Create shortcuts.
@@ -8740,6 +8834,7 @@
         assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0,
                 filter_any));
 
+        mInjectCheckAccessShortcutsPermission = true;
         mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0,
                 CACHE_OWNER_0);
         mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
index 6a2b8e0..c8a4052 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
@@ -117,6 +117,7 @@
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
         });
@@ -216,6 +217,7 @@
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
                     mTestLooper.getNewExecutor());
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
@@ -242,6 +244,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -274,6 +277,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -301,6 +305,7 @@
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
@@ -500,6 +505,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -559,6 +565,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -596,6 +603,7 @@
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -664,6 +672,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -731,6 +740,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -799,6 +809,7 @@
 
         ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
                     CACHE_OWNER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 44b202d..35c513f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -27,7 +27,6 @@
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index 5846fc11..b0423bf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -23,7 +23,6 @@
 import android.app.PropertyInvalidatedCache;
 import android.content.pm.UserInfo;
 import android.os.Looper;
-import android.os.UserManagerInternal;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 2250185..4fac9dc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -44,7 +44,6 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 8e94544..0b44c59 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -43,7 +43,6 @@
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.support.test.uiautomator.UiDevice;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
new file mode 100644
index 0000000..92942bb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.policy;
+
+
+import static android.content.Context.SENSOR_SERVICE;
+
+import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.input.InputManagerInternal;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceStateProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DeviceStateProviderImpl}.
+ * <p/>
+ * Run with <code>atest DeviceStateProviderImplTest</code>.
+ */
+public final class DeviceStateProviderImplTest {
+    private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class);
+    private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
+
+    private Context mContext;
+    private SensorManager mSensorManager;
+
+    @Before
+    public void setup() {
+        LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
+        mContext = mock(Context.class);
+        mSensorManager = mock(SensorManager.class);
+        when(mContext.getSystemServiceName(eq(SensorManager.class))).thenReturn(SENSOR_SERVICE);
+        when(mContext.getSystemService(eq(SENSOR_SERVICE))).thenReturn(mSensorManager);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(InputManagerInternal.class);
+    }
+
+    @Test
+    public void create_noConfig() {
+        assertDefaultProviderValues(null);
+    }
+
+    @Test
+    public void create_emptyFile() {
+        String configString = "";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+
+        assertDefaultProviderValues(config);
+    }
+
+    @Test
+    public void create_emptyConfig() {
+        String configString = "<device-state-config></device-state-config>";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+
+        assertDefaultProviderValues(config);
+    }
+
+    @Test
+    public void create_invalidConfig() {
+        String configString = "<device-state-config>\n"
+                + "    </device-state>\n"
+                + "</device-state-config>\n";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+
+        assertDefaultProviderValues(config);
+    }
+
+    private void assertDefaultProviderValues(
+            @Nullable DeviceStateProviderImpl.ReadableConfig config) {
+        DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+                config);
+
+        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+        provider.setListener(listener);
+
+        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue());
+
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue());
+    }
+
+    @Test
+    public void create_lidSwitch() {
+        String configString = "<device-state-config>\n"
+                + "    <device-state>\n"
+                + "        <identifier>1</identifier>\n"
+                + "        <conditions>\n"
+                + "            <lid-switch>\n"
+                + "                <open>true</open>\n"
+                + "            </lid-switch>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "    <device-state>\n"
+                + "        <identifier>2</identifier>\n"
+                + "        <conditions>\n"
+                + "            <lid-switch>\n"
+                + "                <open>false</open>\n"
+                + "            </lid-switch>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "</device-state-config>\n";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+        DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+                config);
+
+        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+        provider.setListener(listener);
+
+        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(2, mIntegerCaptor.getValue().intValue());
+
+        Mockito.clearInvocations(listener);
+
+        provider.notifyLidSwitchChanged(0, true /* lidOpen */);
+
+        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(1, mIntegerCaptor.getValue().intValue());
+    }
+
+    @Test
+    public void create_sensor() throws Exception {
+        Sensor sensor = newSensor("sensor", Sensor.TYPE_HINGE_ANGLE);
+        when(mSensorManager.getSensorList(eq(sensor.getType()))).thenReturn(List.of(sensor));
+
+        String configString = "<device-state-config>\n"
+                + "    <device-state>\n"
+                + "        <identifier>1</identifier>\n"
+                + "        <conditions>\n"
+                + "            <sensor>\n"
+                + "                <name>" + sensor.getName() + "</name>\n"
+                + "                <type>" + sensor.getType() + "</type>\n"
+                + "                <value>\n"
+                + "                    <max>90</max>\n"
+                + "                </value>\n"
+                + "            </sensor>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "    <device-state>\n"
+                + "        <identifier>2</identifier>\n"
+                + "        <conditions>\n"
+                + "            <sensor>\n"
+                + "                <name>" + sensor.getName() + "</name>\n"
+                + "                <type>" + sensor.getType() + "</type>\n"
+                + "                <value>\n"
+                + "                    <min-inclusive>90</min-inclusive>\n"
+                + "                    <max>180</max>\n"
+                + "                </value>\n"
+                + "            </sensor>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "    <device-state>\n"
+                + "        <identifier>3</identifier>\n"
+                + "        <conditions>\n"
+                + "            <sensor>\n"
+                + "                <name>" + sensor.getName() + "</name>\n"
+                + "                <type>" + sensor.getType() + "</type>\n"
+                + "                <value>\n"
+                + "                    <min-inclusive>180</min-inclusive>\n"
+                + "                </value>\n"
+                + "            </sensor>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "</device-state-config>\n";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+        DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+                config);
+
+        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+        provider.setListener(listener);
+
+        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        assertArrayEquals(new int[] { 1, 2, 3 }, mIntArrayCaptor.getValue());
+
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(1, mIntegerCaptor.getValue().intValue());
+
+        Mockito.clearInvocations(listener);
+
+        SensorEvent event0 = mock(SensorEvent.class);
+        event0.sensor = sensor;
+        FieldSetter.setField(event0, event0.getClass().getField("values"), new float[] { 180 });
+
+        provider.onSensorChanged(event0);
+
+        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(3, mIntegerCaptor.getValue().intValue());
+
+        Mockito.clearInvocations(listener);
+
+        SensorEvent event1 = mock(SensorEvent.class);
+        event1.sensor = sensor;
+        FieldSetter.setField(event1, event1.getClass().getField("values"), new float[] { 90 });
+
+        provider.onSensorChanged(event1);
+
+        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(2, mIntegerCaptor.getValue().intValue());
+
+        Mockito.clearInvocations(listener);
+
+        SensorEvent event2 = mock(SensorEvent.class);
+        event2.sensor = sensor;
+        FieldSetter.setField(event2, event2.getClass().getField("values"), new float[] { 0 });
+
+        provider.onSensorChanged(event2);
+
+        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(1, mIntegerCaptor.getValue().intValue());
+    }
+
+    private static Sensor newSensor(String name, int type) throws Exception {
+        Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor();
+        constructor.setAccessible(true);
+
+        Sensor sensor = constructor.newInstance();
+        FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name);
+        FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mType"), type);
+        return sensor;
+    }
+
+    private static final class TestReadableConfig implements
+            DeviceStateProviderImpl.ReadableConfig {
+        private final byte[] mData;
+
+        TestReadableConfig(String configFileData) {
+            mData = configFileData.getBytes();
+        }
+
+        @NonNull
+        @Override
+        public InputStream openRead() throws IOException {
+            return new ByteArrayInputStream(mData);
+        }
+    }
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index c409438..f69dfe9 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -51,3 +51,17 @@
     resource_dirs: ["res"],
     manifest: "AndroidManifestApp3.xml",
 }
+
+android_test_helper_app {
+    name: "PackageParserTestApp4",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    resource_dirs: ["res"],
+    manifest: "AndroidManifestApp4.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
new file mode 100644
index 0000000..299b9a0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -0,0 +1,65 @@
+<?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="com.android.servicestests.apps.packageparserapp" >
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <property android:name="android.cts.PROPERTY_RESOURCE_XML" android:resource="@xml/xml_property" />
+        <property android:name="android.cts.PROPERTY_RESOURCE_INTEGER" android:resource="@integer/integer_property" />
+        <property android:name="android.cts.PROPERTY_BOOLEAN" android:value="true" />
+        <property android:name="android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE" android:value="@bool/boolean_property" />
+        <property android:name="android.cts.PROPERTY_FLOAT" android:value="3.14" />
+        <property android:name="android.cts.PROPERTY_FLOAT_VIA_RESOURCE" android:value="@dimen/float_property" />
+        <property android:name="android.cts.PROPERTY_INTEGER" android:value="42" />
+        <property android:name="android.cts.PROPERTY_INTEGER_VIA_RESOURCE" android:value="@integer/integer_property" />
+        <property android:name="android.cts.PROPERTY_STRING" android:value="koala" />
+        <property android:name="android.cts.PROPERTY_STRING_VIA_RESOURCE" android:value="@string/string_property" />
+
+	    <activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
+	              android:exported="true" >
+	        <property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
+	        <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
+	        <property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />
+	        <intent-filter>
+	           <action android:name="android.intent.action.MAIN" />
+	           <category android:name="android.intent.category.LAUNCHER" />
+	        </intent-filter>
+	    </activity>
+	    <activity-alias android:name="com.android.servicestests.apps.packageparserapp.MyActivityAlias"
+	                    android:targetActivity="com.android.servicestests.apps.packageparserapp.MyActivity">
+	        <property android:name="android.cts.PROPERTY_ACTIVITY_ALIAS" android:value="@integer/integer_property" />
+	        <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
+	    </activity-alias>
+	    <provider android:name="com.android.servicestests.apps.packageparserapp.MyProvider"
+	             android:authorities="propertytest">
+	        <property android:name="android.cts.PROPERTY_PROVIDER" android:value="@integer/integer_property" />
+	    </provider>
+	    <receiver android:name="com.android.servicestests.apps.packageparserapp.MyReceiver">
+	        <property android:name="android.cts.PROPERTY_RECEIVER" android:value="@integer/integer_property" />
+	        <property android:name="android.cts.PROPERTY_STRING" android:value="koala receiver" />
+	    </receiver>
+	    <service android:name="com.android.servicestests.apps.packageparserapp.MyService">
+	        <property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" />
+	        <property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" />
+	    </service>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.servicestests.apps.packageparserapp" />
+</manifest>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
index 6a4cc65..67ecf66 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
@@ -16,4 +16,10 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <bool name="config_isIsolated">true</bool>
-</resources>
\ No newline at end of file
+    <bool name="boolean_property">true</bool>
+    <color name="color_property">#00FF00</color>
+    <item name="float_property" format="float" type="dimen">2.718</item>
+    <dimen name="dimen_property">23dp</dimen>
+    <integer name="integer_property">123</integer>
+    <string name="string_property">giraffe</string>
+</resources>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml
new file mode 100644
index 0000000..588db8d
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<paths>
+    <external-path path="Android/data/" name="files_root" />
+    <external-path path="." name="external_storage_root" />
+</paths>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java
new file mode 100644
index 0000000..6627166
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.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.servicestests.apps.packageparserapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class MyProvider extends ContentProvider {
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "text/plain";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
index 36ac5d5..ce6939a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
@@ -89,7 +89,7 @@
         when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
 
         Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
-                PendingIntent.getActivity(mContext, 0, new Intent(), 0),
+                PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE),
                         Icon.createWithResource("", 0)).build();
 
         int flags = metadata.getFlags();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9766cb5..00f706b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.notification;
 
+import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.GROUP_ALERT_ALL;
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
@@ -38,6 +39,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
@@ -50,9 +52,11 @@
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Color;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -1596,6 +1600,77 @@
         assertTrue(interrupter.isInterruptive());
     }
 
+    @Test
+    public void testBubbleSuppressedNotificationDoesntMakeSound() {
+        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+                        mock(PendingIntent.class), mock(Icon.class))
+                .build();
+
+        NotificationRecord record = getBuzzyNotification();
+        metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+        record.getNotification().setBubbleMetadata(metadata);
+        record.setAllowBubble(true);
+        record.getNotification().flags |= FLAG_BUBBLE;
+        record.isUpdate = true;
+        record.setInterruptive(false);
+
+        mService.buzzBeepBlinkLocked(record);
+        verifyNeverVibrate();
+    }
+
+    @Test
+    public void testOverflowBubbleSuppressedNotificationDoesntMakeSound() {
+        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+                mock(PendingIntent.class), mock(Icon.class))
+                .build();
+
+        NotificationRecord record = getBuzzyNotification();
+        metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+        record.getNotification().setBubbleMetadata(metadata);
+        record.setFlagBubbleRemoved(true);
+        record.setAllowBubble(true);
+        record.isUpdate = true;
+        record.setInterruptive(false);
+
+        mService.buzzBeepBlinkLocked(record);
+        verifyNeverVibrate();
+    }
+
+    @Test
+    public void testBubbleUpdateMakesSound() {
+        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+                mock(PendingIntent.class), mock(Icon.class))
+                .build();
+
+        NotificationRecord record = getBuzzyNotification();
+        record.getNotification().setBubbleMetadata(metadata);
+        record.setAllowBubble(true);
+        record.getNotification().flags |= FLAG_BUBBLE;
+        record.isUpdate = true;
+        record.setInterruptive(true);
+
+        mService.buzzBeepBlinkLocked(record);
+        verifyVibrate(1);
+    }
+
+    @Test
+    public void testNewBubbleSuppressedNotifMakesSound() {
+        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+                mock(PendingIntent.class), mock(Icon.class))
+                .build();
+
+        NotificationRecord record = getBuzzyNotification();
+        metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+        record.getNotification().setBubbleMetadata(metadata);
+        record.setAllowBubble(true);
+        record.getNotification().flags |= FLAG_BUBBLE;
+        record.isUpdate = false;
+        record.setInterruptive(true);
+
+        mService.buzzBeepBlinkLocked(record);
+        verifyVibrate(1);
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 338ed06..f1dc098 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -31,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.timeout;
 
 import android.app.ActivityOptions;
@@ -53,6 +54,7 @@
 
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
+import java.util.function.ToIntFunction;
 
 /**
  * Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -158,6 +160,41 @@
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
+    @Test
+    public void testLaunchState() {
+        final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+            clearInvocations(mLaunchObserver);
+            onActivityLaunched(mTopActivity);
+            notifyTransitionStarting(mTopActivity);
+            if (doRelaunch) {
+                mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+            }
+            final ActivityMetricsLogger.TransitionInfoSnapshot info =
+                    notifyWindowsDrawn(mTopActivity);
+            verifyOnActivityLaunchFinished(mTopActivity);
+            return info.getLaunchState();
+        };
+
+        final WindowProcessController app = mTopActivity.app;
+        // Assume that the process is started (ActivityBuilder has mocked the returned value of
+        // ATMS#getProcessController) but the activity has not attached process.
+        mTopActivity.app = null;
+        assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+                .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+
+        mTopActivity.app = app;
+        assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+                .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
+
+        assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+                .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
+
+        mTopActivity.app = null;
+        doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
+        assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+                .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+    }
+
     private void onActivityLaunched(ActivityRecord activity) {
         onIntentStarted(activity.intent);
         notifyActivityLaunched(START_SUCCESS, activity);
@@ -168,15 +205,10 @@
 
     @Test
     public void testOnActivityLaunchFinished() {
-        // Assume that the process is started (ActivityBuilder has mocked the returned value of
-        // ATMS#getProcessController) but the activity has not attached process.
-        mTopActivity.app = null;
         onActivityLaunched(mTopActivity);
 
         notifyTransitionStarting(mTopActivity);
-        final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity);
-        assertWithMessage("Warm launch").that(info.getLaunchState())
-                .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+        notifyWindowsDrawn(mTopActivity);
 
         verifyOnActivityLaunchFinished(mTopActivity);
         verifyNoMoreInteractions(mLaunchObserver);
@@ -231,8 +263,6 @@
         assertWithMessage("Record start source").that(info.sourceType)
                 .isEqualTo(SourceInfo.TYPE_LAUNCHER);
         assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10);
-        assertWithMessage("Hot launch").that(info.getLaunchState())
-                .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
 
         verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
         verifyOnActivityLaunchFinished(mTopActivity);
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 bb526695..53ade0e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,10 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -32,7 +30,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.os.Process.NOBODY_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -125,61 +123,65 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class ActivityRecordTests extends WindowTestsBase {
-    private Task mStack;
-    private Task mTask;
-    private ActivityRecord mActivity;
 
     @Before
     public void setUp() throws Exception {
-        mTask = new TaskBuilder(mSupervisor)
-                .setCreateParentTask(true).setCreateActivity(true).build();
-        mStack = mTask.getRootTask();
-        mActivity = mTask.getTopNonFinishingActivity();
-
         setBooted(mAtm);
     }
 
     @Test
     public void testStackCleanupOnClearingTask() {
-        mActivity.onParentChanged(null /*newParent*/, mActivity.getTask());
-        verify(mStack, times(1)).cleanUpActivityReferences(any());
+        final ActivityRecord activity = createActivityWith2LevelTask();
+        final Task task = activity.getTask();
+        final Task rootTask = activity.getRootTask();
+        activity.onParentChanged(null /*newParent*/, task);
+        verify(rootTask, times(1)).cleanUpActivityReferences(any());
     }
 
     @Test
     public void testStackCleanupOnActivityRemoval() {
-        mTask.removeChild(mActivity);
-        verify(mStack, times(1)).cleanUpActivityReferences(any());
+        final ActivityRecord activity = createActivityWith2LevelTask();
+        final Task task = activity.getTask();
+        final Task rootTask = activity.getRootTask();
+        task.removeChild(activity);
+        verify(rootTask, times(1)).cleanUpActivityReferences(any());
     }
 
     @Test
     public void testStackCleanupOnTaskRemoval() {
-        mStack.removeChild(mTask, null /*reason*/);
-        // Stack should be gone on task removal.
-        assertNull(mAtm.mRootWindowContainer.getStack(mStack.mTaskId));
+        final ActivityRecord activity = createActivityWith2LevelTask();
+        final Task task = activity.getTask();
+        final Task rootTask = activity.getRootTask();
+        rootTask.removeChild(task, null /*reason*/);
+        // parentTask should be gone on task removal.
+        assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId));
     }
 
     @Test
     public void testRemoveChildWithOverlayActivity() {
-        final ActivityRecord overlayActivity =
-                new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task).build();
         overlayActivity.setTaskOverlay(true);
-        final ActivityRecord overlayActivity2 =
-                new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord overlayActivity2 = new ActivityBuilder(mAtm).setTask(task).build();
         overlayActivity2.setTaskOverlay(true);
 
-        mTask.removeChild(overlayActivity2, "test");
+        task.removeChild(overlayActivity2, "test");
         verify(mSupervisor, never()).removeTask(any(), anyBoolean(), anyBoolean(), any());
     }
 
     @Test
     public void testNoCleanupMovingActivityInSameStack() {
-        final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
-        mActivity.reparent(newTask, 0, null /*reason*/);
-        verify(mStack, times(0)).cleanUpActivityReferences(any());
+        final ActivityRecord activity = createActivityWith2LevelTask();
+        final Task rootTask = activity.getRootTask();
+        final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build();
+        activity.reparent(newTask, 0, null /*reason*/);
+        verify(rootTask, times(0)).cleanUpActivityReferences(any());
     }
 
     @Test
     public void testPausingWhenVisibleFromStopped() throws Exception {
+        final ActivityRecord activity = createActivityWithTask();
         final MutableBoolean pauseFound = new MutableBoolean(false);
         doAnswer((InvocationOnMock invocationOnMock) -> {
             final ClientTransaction transaction = invocationOnMock.getArgument(0);
@@ -187,49 +189,50 @@
                 pauseFound.value = true;
             }
             return null;
-        }).when(mActivity.app.getThread()).scheduleTransaction(any());
+        }).when(activity.app.getThread()).scheduleTransaction(any());
 
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
+        activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
 
         // The activity is in the focused stack so it should be resumed.
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(RESUMED));
+        activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(activity.isState(RESUMED));
         assertFalse(pauseFound.value);
 
         // Make the activity non focusable
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
-        doReturn(false).when(mActivity).isFocusable();
+        activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
+        doReturn(false).when(activity).isFocusable();
 
         // If the activity is not focusable, it should move to paused.
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(PAUSING));
+        activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(activity.isState(PAUSING));
         assertTrue(pauseFound.value);
 
         // Make sure that the state does not change for current non-stopping states.
-        mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
-        doReturn(true).when(mActivity).isFocusable();
+        activity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
+        doReturn(true).when(activity).isFocusable();
 
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
 
-        assertTrue(mActivity.isState(INITIALIZING));
+        assertTrue(activity.isState(INITIALIZING));
 
         // Make sure the state does not change if we are not the current top activity.
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
+        activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
 
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        mStack.mTranslucentActivityWaiting = topActivity;
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(STARTED));
+        final Task task = activity.getTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        task.mTranslucentActivityWaiting = topActivity;
+        activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(activity.isState(STARTED));
 
-        mStack.mTranslucentActivityWaiting = null;
+        task.mTranslucentActivityWaiting = null;
         topActivity.setOccludesParent(false);
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(STARTED));
+        activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
+        activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(activity.isState(STARTED));
     }
 
-    private void ensureActivityConfiguration() {
-        mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+    private void ensureActivityConfiguration(ActivityRecord activity) {
+        activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
     }
 
     @Test
@@ -245,136 +248,145 @@
 
     @Test
     public void testsApplyOptionsLocked() {
+        final ActivityRecord activity = createActivityWithTask();
         ActivityOptions activityOptions = ActivityOptions.makeBasic();
 
         // Set and apply options for ActivityRecord. Pending options should be cleared
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
+        activity.updateOptionsLocked(activityOptions);
+        activity.applyOptionsLocked();
+        assertNull(activity.pendingOptions);
 
         // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options.
         // Pending options should be cleared for both ActivityRecords
-        ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(mTask).build();
+        ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
         activity2.updateOptionsLocked(activityOptions);
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
+        activity.updateOptionsLocked(activityOptions);
+        activity.applyOptionsLocked();
+        assertNull(activity.pendingOptions);
         assertNull(activity2.pendingOptions);
 
         // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
         // Pending options should be cleared for only ActivityRecord that was applied
-        Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
-        activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
+        activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity2.updateOptionsLocked(activityOptions);
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
+        activity.updateOptionsLocked(activityOptions);
+        activity.applyOptionsLocked();
+        assertNull(activity.pendingOptions);
         assertNotNull(activity2.pendingOptions);
     }
 
     @Test
     public void testNewOverrideConfigurationIncrementsSeq() {
+        final ActivityRecord activity = createActivityWithTask();
         final Configuration newConfig = new Configuration();
 
-        final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
-        mActivity.onRequestedOverrideConfigurationChanged(newConfig);
-        assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+        final int prevSeq = activity.getMergedOverrideConfiguration().seq;
+        activity.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq);
     }
 
     @Test
     public void testNewParentConfigurationIncrementsSeq() {
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
         final Configuration newConfig = new Configuration(
-                mTask.getRequestedOverrideConfiguration());
+                task.getRequestedOverrideConfiguration());
         newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
                 ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
 
-        final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
-        mTask.onRequestedOverrideConfigurationChanged(newConfig);
-        assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+        final int prevSeq = activity.getMergedOverrideConfiguration().seq;
+        task.onRequestedOverrideConfigurationChanged(newConfig);
+        assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq);
     }
 
     @Test
     public void testSetsRelaunchReason_NotDragResizing() {
-        mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.RESUMED, "Testing");
 
-        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
-        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                mActivity.getConfiguration()));
+        task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                activity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
-        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        activity.info.configChanges &= ~CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(task.getConfiguration());
         newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
                 ? ORIENTATION_LANDSCAPE
                 : ORIENTATION_PORTRAIT;
-        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+        task.onRequestedOverrideConfigurationChanged(newConfig);
 
-        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+        activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
-        ensureActivityConfiguration();
+        ensureActivityConfiguration(activity);
 
         assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE,
-                mActivity.mRelaunchReason);
+                activity.mRelaunchReason);
     }
 
     @Test
     public void testSetsRelaunchReason_DragResizing() {
-        mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.RESUMED, "Testing");
 
-        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
-        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                mActivity.getConfiguration()));
+        task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                activity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
-        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        activity.info.configChanges &= ~CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(task.getConfiguration());
         newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
                 ? ORIENTATION_LANDSCAPE
                 : ORIENTATION_PORTRAIT;
-        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+        task.onRequestedOverrideConfigurationChanged(newConfig);
 
-        doReturn(true).when(mTask).isDragResizing();
+        doReturn(true).when(task).isDragResizing();
 
-        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+        activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
-        ensureActivityConfiguration();
+        ensureActivityConfiguration(activity);
 
         assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE,
-                mActivity.mRelaunchReason);
+                activity.mRelaunchReason);
     }
 
     @Test
     public void testSetsRelaunchReason_NonResizeConfigChanges() {
-        mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.RESUMED, "Testing");
 
-        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
-        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                mActivity.getConfiguration()));
+        task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                activity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
-        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
+        final Configuration newConfig = new Configuration(task.getConfiguration());
         newConfig.fontScale = 5;
-        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+        task.onRequestedOverrideConfigurationChanged(newConfig);
 
-        mActivity.mRelaunchReason =
+        activity.mRelaunchReason =
                 ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 
-        ensureActivityConfiguration();
+        ensureActivityConfiguration(activity);
 
         assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE,
-                mActivity.mRelaunchReason);
+                activity.mRelaunchReason);
     }
 
     @Test
     public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
-        mActivity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
                 .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
                 .build();
-        mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(Task.ActivityState.RESUMED, "Testing");
 
-        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                mActivity.getConfiguration()));
+        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                activity.getConfiguration()));
 
-        final Configuration newConfig = new Configuration(mActivity.getConfiguration());
+        final Configuration newConfig = new Configuration(activity.getConfiguration());
         final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
         final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
         if (newConfig.orientation == ORIENTATION_PORTRAIT) {
@@ -388,7 +400,7 @@
         }
 
         // Mimic the behavior that display doesn't handle app's requested orientation.
-        final DisplayContent dc = mTask.getDisplayContent();
+        final DisplayContent dc = activity.getTask().getDisplayContent();
         doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
         doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
 
@@ -404,24 +416,26 @@
                 throw new IllegalStateException("Orientation in new config should be either"
                         + "landscape or portrait.");
         }
-        mActivity.setRequestedOrientation(requestedOrientation);
+        activity.setRequestedOrientation(requestedOrientation);
 
         final ActivityConfigurationChangeItem expected =
                 ActivityConfigurationChangeItem.obtain(newConfig);
-        verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()),
-                eq(mActivity.appToken), eq(expected));
+        verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
+                eq(activity.appToken), eq(expected));
     }
 
     @Test
     public void ignoreRequestedOrientationInFreeformWindows() {
-        mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
         final Rect stableRect = new Rect();
-        mStack.mDisplayContent.getStableRect(stableRect);
+        task.mDisplayContent.getStableRect(stableRect);
 
         // Carve out non-decor insets from stableRect
         final Rect insets = new Rect();
-        final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
-        final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
+        final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
+        final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
         policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
                 displayInfo.logicalHeight, displayInfo.displayCutout, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -440,27 +454,30 @@
             bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2;
             bounds.right = bounds.left + newWidth;
         }
-        mTask.setBounds(bounds);
+        task.setBounds(bounds);
 
         // Requests orientation that's different from its bounds.
-        mActivity.setRequestedOrientation(
+        activity.setRequestedOrientation(
                 isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
 
         // Asserts it has orientation derived from bounds.
         assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
-                mActivity.getConfiguration().orientation);
+                activity.getConfiguration().orientation);
     }
 
     @Test
     public void ignoreRequestedOrientationInSplitWindows() {
-        mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final ActivityRecord activity = createActivityWith2LevelTask();
+        final Task task = activity.getTask();
+        final Task rootTask = activity.getRootTask();
+        rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final Rect stableRect = new Rect();
-        mStack.mDisplayContent.getStableRect(stableRect);
+        rootTask.mDisplayContent.getStableRect(stableRect);
 
         // Carve out non-decor insets from stableRect
         final Rect insets = new Rect();
-        final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
-        final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
+        final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
+        final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
         policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
                 displayInfo.logicalHeight, displayInfo.displayCutout, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -479,85 +496,91 @@
             bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2;
             bounds.right = bounds.left + newWidth;
         }
-        mTask.setBounds(bounds);
+        task.setBounds(bounds);
 
         // Requests orientation that's different from its bounds.
-        mActivity.setRequestedOrientation(
+        activity.setRequestedOrientation(
                 isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
 
         // Asserts it has orientation derived from bounds.
         assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
-                mActivity.getConfiguration().orientation);
+                activity.getConfiguration().orientation);
     }
 
     @Test
     public void testShouldMakeActive_deferredResume() {
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
 
         mSupervisor.beginDeferResume();
-        assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+        assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
 
         mSupervisor.endDeferResume();
-        assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */));
+        assertEquals(true, activity.shouldMakeActive(null /* activeActivity */));
     }
 
     @Test
     public void testShouldMakeActive_nonTopVisible() {
-        ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
         finishingActivity.finishing = true;
-        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
 
-        assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+        assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
     }
 
     @Test
     public void testShouldResume_stackVisibility() {
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
-        spyOn(mStack);
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
 
-        doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
-        assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
+        doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+        assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
 
-        doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null);
-        assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
+        doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+        assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
 
-        doReturn(TASK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null);
-        assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
+        doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+        assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
     }
 
     @Test
     public void testShouldResumeOrPauseWithResults() {
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
-        spyOn(mStack);
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
 
-        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        mActivity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
+        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
         topActivity.finishing = true;
 
-        doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
-        assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
-        assertEquals(false, mActivity.shouldPauseActivity(null /*activeActivity */));
+        doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+        assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
+        assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
     }
 
     @Test
     public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
-        mActivity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
                 .setLaunchTaskBehind(true)
                 .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
                 .build();
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+        final Task task = activity.getTask();
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
 
         final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         try {
             doReturn(false).when(stack).isTranslucent(any());
-            assertTrue(mStack.shouldBeVisible(null /* starting */));
+            assertTrue(task.shouldBeVisible(null /* starting */));
 
-            mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                    mActivity.getConfiguration()));
+            activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                    activity.getConfiguration()));
 
-            final Configuration newConfig = new Configuration(mActivity.getConfiguration());
+            final Configuration newConfig = new Configuration(activity.getConfiguration());
             final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
             final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
             if (newConfig.orientation == ORIENTATION_PORTRAIT) {
@@ -570,15 +593,15 @@
                 newConfig.screenHeightDp = longSide;
             }
 
-            mTask.onConfigurationChanged(newConfig);
+            task.onConfigurationChanged(newConfig);
 
-            mActivity.ensureActivityConfiguration(0 /* globalChanges */,
+            activity.ensureActivityConfiguration(0 /* globalChanges */,
                     false /* preserveWindow */, true /* ignoreStopState */);
 
             final ActivityConfigurationChangeItem expected =
                     ActivityConfigurationChangeItem.obtain(newConfig);
             verify(mAtm.getLifecycleManager()).scheduleTransaction(
-                    eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected));
+                    eq(activity.app.getThread()), eq(activity.appToken), eq(expected));
         } finally {
             stack.getDisplayArea().removeChild(stack);
         }
@@ -586,16 +609,18 @@
 
     @Test
     public void testShouldStartWhenMakeClientActive() {
-        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
         topActivity.setOccludesParent(false);
-        mActivity.setState(Task.ActivityState.STOPPED, "Testing");
-        mActivity.setVisibility(true);
-        mActivity.makeActiveIfNeeded(null /* activeActivity */);
-        assertEquals(STARTED, mActivity.getState());
+        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setVisibility(true);
+        activity.makeActiveIfNeeded(null /* activeActivity */);
+        assertEquals(STARTED, activity.getState());
     }
 
     @Test
     public void testTakeOptions() {
+        final ActivityRecord activity = createActivityWithTask();
         ActivityOptions opts = ActivityOptions.makeRemoteAnimation(
                 new RemoteAnimationAdapter(new Stub() {
 
@@ -611,13 +636,13 @@
 
                     }
                 }, 0, 0));
-        mActivity.updateOptionsLocked(opts);
-        assertNotNull(mActivity.takeOptionsLocked(true /* fromClient */));
-        assertNotNull(mActivity.pendingOptions);
+        activity.updateOptionsLocked(opts);
+        assertNotNull(activity.takeOptionsLocked(true /* fromClient */));
+        assertNotNull(activity.pendingOptions);
 
-        mActivity.updateOptionsLocked(ActivityOptions.makeBasic());
-        assertNotNull(mActivity.takeOptionsLocked(false /* fromClient */));
-        assertNull(mActivity.pendingOptions);
+        activity.updateOptionsLocked(ActivityOptions.makeBasic());
+        assertNotNull(activity.takeOptionsLocked(false /* fromClient */));
+        assertNull(activity.pendingOptions);
     }
 
     @Test
@@ -626,7 +651,7 @@
                 Resources.getSystem().getString(R.string.config_chooserActivity));
         ActivityRecord chooserActivity = new ActivityBuilder(mAtm).setComponent(
                 chooserComponent).build();
-        assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue();
+        assertThat(chooserActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue();
     }
 
     /**
@@ -635,49 +660,52 @@
      */
     @Test
     public void testHasSavedState() {
-        assertTrue(mActivity.hasSavedState());
+        final ActivityRecord activity = createActivityWithTask();
+        assertTrue(activity.hasSavedState());
 
-        ActivityRecord.activityResumedLocked(mActivity.appToken);
-        assertFalse(mActivity.hasSavedState());
-        assertNull(mActivity.getSavedState());
+        ActivityRecord.activityResumedLocked(activity.appToken);
+        assertFalse(activity.hasSavedState());
+        assertNull(activity.getSavedState());
     }
 
     /** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */
     @Test
     public void testUpdateSavedState() {
-        mActivity.setSavedState(null /* savedState */);
-        assertFalse(mActivity.hasSavedState());
-        assertNull(mActivity.getSavedState());
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setSavedState(null /* savedState */);
+        assertFalse(activity.hasSavedState());
+        assertNull(activity.getSavedState());
 
         final Bundle savedState = new Bundle();
         savedState.putString("test", "string");
-        mActivity.setSavedState(savedState);
-        assertTrue(mActivity.hasSavedState());
-        assertEquals(savedState, mActivity.getSavedState());
+        activity.setSavedState(savedState);
+        assertTrue(activity.hasSavedState());
+        assertEquals(savedState, activity.getSavedState());
     }
 
     /** Verify the correct updates of saved state when activity client reports stop. */
     @Test
     public void testUpdateSavedState_activityStopped() {
+        final ActivityRecord activity = createActivityWithTask();
         final Bundle savedState = new Bundle();
         savedState.putString("test", "string");
         final PersistableBundle persistentSavedState = new PersistableBundle();
         persistentSavedState.putString("persist", "string");
 
         // Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored.
-        mActivity.setState(STOPPING, "test");
-        mActivity.activityStopped(savedState, persistentSavedState, "desc");
-        assertTrue(mActivity.hasSavedState());
-        assertEquals(savedState, mActivity.getSavedState());
-        assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+        activity.setState(STOPPING, "test");
+        activity.activityStopped(savedState, persistentSavedState, "desc");
+        assertTrue(activity.hasSavedState());
+        assertEquals(savedState, activity.getSavedState());
+        assertEquals(persistentSavedState, activity.getPersistentSavedState());
 
         // Sending 'null' for saved state can only happen due to timeout, so previously stored saved
         // states should not be overridden.
-        mActivity.setState(STOPPING, "test");
-        mActivity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc");
-        assertTrue(mActivity.hasSavedState());
-        assertEquals(savedState, mActivity.getSavedState());
-        assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+        activity.setState(STOPPING, "test");
+        activity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc");
+        assertTrue(activity.hasSavedState());
+        assertEquals(savedState, activity.getSavedState());
+        assertEquals(persistentSavedState, activity.getPersistentSavedState());
     }
 
     /**
@@ -686,19 +714,20 @@
      */
     @Test
     public void testFinishActivityIfPossible_cancelled() {
+        final ActivityRecord activity = createActivityWithTask();
         // Mark activity as finishing
-        mActivity.finishing = true;
+        activity.finishing = true;
         assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertTrue(mActivity.finishing);
-        assertTrue(mActivity.isInStackLocked());
+                activity.finishIfPossible("test", false /* oomAdj */));
+        assertTrue(activity.finishing);
+        assertTrue(activity.isInStackLocked());
 
         // Remove activity from task
-        mActivity.finishing = false;
-        mActivity.onParentChanged(null /*newParent*/, mActivity.getTask());
+        activity.finishing = false;
+        activity.onParentChanged(null /*newParent*/, activity.getTask());
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertFalse(mActivity.finishing);
+                activity.finishIfPossible("test", false /* oomAdj */));
+        assertFalse(activity.finishing);
     }
 
     /**
@@ -707,20 +736,21 @@
      */
     @Test
     public void testFinishActivityIfPossible_requested() {
-        mActivity.finishing = false;
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = false;
         assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertTrue(mActivity.finishing);
-        assertTrue(mActivity.isInStackLocked());
+                activity.finishIfPossible("test", false /* oomAdj */));
+        assertTrue(activity.finishing);
+        assertTrue(activity.isInStackLocked());
 
         // First request to finish activity must schedule a "destroy" request to the client.
         // Activity must be removed from history after the client reports back or after timeout.
-        mActivity.finishing = false;
-        mActivity.setState(STOPPED, "test");
+        activity.finishing = false;
+        activity.setState(STOPPED, "test");
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertTrue(mActivity.finishing);
-        assertTrue(mActivity.isInStackLocked());
+                activity.finishIfPossible("test", false /* oomAdj */));
+        assertTrue(activity.finishing);
+        assertTrue(activity.isInStackLocked());
     }
 
     /**
@@ -728,26 +758,28 @@
      */
     @Test
     public void testFinishActivityIfPossible_removed() {
+        final ActivityRecord activity = createActivityWithTask();
         // Prepare the activity record to be ready for immediate removal. It should be invisible and
         // have no process. Otherwise, request to finish it will send a message to client first.
-        mActivity.setState(STOPPED, "test");
-        mActivity.mVisibleRequested = false;
-        mActivity.nowVisible = false;
+        activity.setState(STOPPED, "test");
+        activity.mVisibleRequested = false;
+        activity.nowVisible = false;
         // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
         // this will cause NPE when updating task's process.
-        mActivity.app = null;
+        activity.app = null;
 
         // Put a visible activity on top, so the finishing activity doesn't have to wait until the
         // next activity reports idle to destroy it.
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = true;
         topActivity.nowVisible = true;
         topActivity.setState(RESUMED, "test");
 
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertTrue(mActivity.finishing);
-        assertFalse(mActivity.isInStackLocked());
+                activity.finishIfPossible("test", false /* oomAdj */));
+        assertTrue(activity.finishing);
+        assertFalse(activity.isInStackLocked());
     }
 
     /**
@@ -756,24 +788,26 @@
      */
     @Test
     public void testFinishActivityIfPossible_adjustStackOrder() {
-        // Prepare the stacks with order (top to bottom): mStack, stack1, stack2.
-        final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
-        mStack.moveToFront("test");
-        // The stack2 is needed here for moving back to simulate the
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        // Prepare the tasks with order (top to bottom): task, task1, task2.
+        final Task task1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        task.moveToFront("test");
+        // The task2 is needed here for moving back to simulate the
         // {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so
         // {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible
-        // stacks. Then when mActivity is finishing, its stack will be invisible (no running
-        // activities in the stack) that is the key condition to verify.
-        final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
-        stack2.moveToBack("test", stack2.getBottomMostTask());
+        // tasks. Then when mActivity is finishing, its task will be invisible (no running
+        // activities in the task) that is the key condition to verify.
+        final Task task2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        task2.moveToBack("test", task2.getBottomMostTask());
 
-        assertTrue(mStack.isTopStackInDisplayArea());
+        assertTrue(task.isTopStackInDisplayArea());
 
-        mActivity.setState(RESUMED, "test");
-        mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
+        activity.setState(RESUMED, "test");
+        activity.finishIfPossible(0 /* resultCode */, null /* resultData */,
                 null /* resultGrants */, "test", false /* oomAdj */);
 
-        assertTrue(stack1.isTopStackInDisplayArea());
+        assertTrue(task1.isTopStackInDisplayArea());
     }
 
     /**
@@ -783,22 +817,25 @@
     @Test
     public void testFinishActivityIfPossible_adjustStackOrderOrganizedRoot() {
         // Make mStack be a the root task that created by task organizer
-        mStack.mCreatedByOrganizer = true;
+        final Task rootableTask = new TaskBuilder(mSupervisor)
+                .setCreateParentTask(true).setCreateActivity(true).build();
+        final Task rootTask = rootableTask.getRootTask();
+        rootTask.mCreatedByOrganizer = true;
 
-        // Have two tasks (topRootableTask and mTask) as the children of mStack.
-        ActivityRecord topActivity = new ActivityBuilder(mActivity.mAtmService)
+        // Have two tasks (topRootableTask and rootableTask) as the children of rootTask.
+        ActivityRecord topActivity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
-                .setParentTask(mStack)
+                .setParentTask(rootTask)
                 .build();
         Task topRootableTask = topActivity.getTask();
         topRootableTask.moveToFront("test");
-        assertTrue(mStack.isTopStackInDisplayArea());
+        assertTrue(rootTask.isTopStackInDisplayArea());
 
         // Finish top activity and verify the next focusable rootable task has adjusted to top.
         topActivity.setState(RESUMED, "test");
         topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
                 null /* resultGrants */, "test", false /* oomAdj */);
-        assertEquals(mTask, mStack.getTopMostTask());
+        assertEquals(rootableTask, rootTask.getTopMostTask());
     }
 
     /**
@@ -808,6 +845,8 @@
      */
     @Test
     public void testFinishActivityIfPossible_PreferredTopStackChanged() {
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
         final ActivityRecord topActivityOnNonTopDisplay =
                 createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
         Task topRootableTask = topActivityOnNonTopDisplay.getRootTask();
@@ -830,8 +869,8 @@
         topActivityOnNonTopDisplay.setState(RESUMED, "test");
         topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */,
                 null /* resultGrants */, "test", false /* oomAdj */);
-        assertEquals(mTask, mStack.getTopMostTask());
-        assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack);
+        assertEquals(task, task.getTopMostTask());
+        assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack);
     }
 
     /**
@@ -839,15 +878,14 @@
      */
     @Test
     public void testFinishActivityIfPossible_resumedStartsPausing() {
-        mActivity.finishing = false;
-        mActivity.setState(RESUMED, "test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = false;
+        activity.setState(RESUMED, "test");
         assertEquals("Currently resumed activity must be paused before removal",
-                FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
-        assertEquals(PAUSING, mActivity.getState());
-        verify(mActivity).setVisibility(eq(false));
-        verify(mActivity.mDisplayContent)
-                .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
-                        eq(false) /* alwaysKeepCurrent */);
+                FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */));
+        assertEquals(PAUSING, activity.getState());
+        verify(activity).setVisibility(eq(false));
+        verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
     }
 
     /**
@@ -855,14 +893,15 @@
      */
     @Test
     public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
+        final ActivityRecord activity = createActivityWithTask();
         final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
         for (ActivityState state : states) {
-            mActivity.finishing = false;
-            mActivity.setState(state, "test");
-            reset(mActivity);
+            activity.finishing = false;
+            activity.setState(state, "test");
+            reset(activity);
             assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
-                    mActivity.finishIfPossible("test", false /* oomAdj */));
-            verify(mActivity).completeFinishing(anyString());
+                    activity.finishIfPossible("test", false /* oomAdj */));
+            verify(activity).completeFinishing(anyString());
         }
     }
 
@@ -871,11 +910,12 @@
      */
     @Test
     public void testFinishActivityIfPossible_pausing() {
-        mActivity.finishing = false;
-        mActivity.setState(PAUSING, "test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = false;
+        activity.setState(PAUSING, "test");
         assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
-                mActivity.finishIfPossible("test", false /* oomAdj */));
-        verify(mActivity, never()).completeFinishing(anyString());
+                activity.finishIfPossible("test", false /* oomAdj */));
+        verify(activity, never()).completeFinishing(anyString());
     }
 
     /**
@@ -884,16 +924,16 @@
      */
     @Test
     public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
-        mActivity.finishing = false;
-        mActivity.mVisibleRequested = true;
-        mActivity.setState(RESUMED, "test");
-        mActivity.finishIfPossible("test", false /* oomAdj */);
+        final ActivityRecord activity = createActivityWithTask();
+        clearInvocations(activity.mDisplayContent);
+        activity.finishing = false;
+        activity.mVisibleRequested = true;
+        activity.setState(RESUMED, "test");
+        activity.finishIfPossible("test", false /* oomAdj */);
 
-        verify(mActivity).setVisibility(eq(false));
-        verify(mActivity.mDisplayContent)
-                .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
-                        eq(false) /* alwaysKeepCurrent */);
-        verify(mActivity.mDisplayContent, never()).executeAppTransition();
+        verify(activity).setVisibility(eq(false));
+        verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
+        verify(activity.mDisplayContent, never()).executeAppTransition();
     }
 
     /**
@@ -901,16 +941,16 @@
      */
     @Test
     public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
-        mActivity.finishing = false;
-        mActivity.mVisibleRequested = true;
-        mActivity.setState(PAUSED, "test");
-        mActivity.finishIfPossible("test", false /* oomAdj */);
+        final ActivityRecord activity = createActivityWithTask();
+        clearInvocations(activity.mDisplayContent);
+        activity.finishing = false;
+        activity.mVisibleRequested = true;
+        activity.setState(PAUSED, "test");
+        activity.finishIfPossible("test", false /* oomAdj */);
 
-        verify(mActivity, atLeast(1)).setVisibility(eq(false));
-        verify(mActivity.mDisplayContent)
-                .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
-                        eq(false) /* alwaysKeepCurrent */);
-        verify(mActivity.mDisplayContent).executeAppTransition();
+        verify(activity, atLeast(1)).setVisibility(eq(false));
+        verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
+        verify(activity.mDisplayContent).executeAppTransition();
     }
 
     /**
@@ -918,17 +958,16 @@
      */
     @Test
     public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
+        final ActivityRecord activity = createActivityWithTask();
         // Put an activity on top of test activity to make it invisible and prevent us from
         // accidentally resuming the topmost one again.
         new ActivityBuilder(mAtm).build();
-        mActivity.mVisibleRequested = false;
-        mActivity.setState(STOPPED, "test");
+        activity.mVisibleRequested = false;
+        activity.setState(STOPPED, "test");
 
-        mActivity.finishIfPossible("test", false /* oomAdj */);
+        activity.finishIfPossible("test", false /* oomAdj */);
 
-        verify(mActivity.mDisplayContent, never())
-                .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
-                        eq(false) /* alwaysKeepCurrent */);
+        verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE));
     }
 
     /**
@@ -936,8 +975,9 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testCompleteFinishing_failNotFinishing() {
-        mActivity.finishing = false;
-        mActivity.completeFinishing("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = false;
+        activity.completeFinishing("test");
     }
 
     /**
@@ -945,8 +985,9 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testCompleteFinishing_failResumed() {
-        mActivity.setState(RESUMED, "test");
-        mActivity.completeFinishing("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(RESUMED, "test");
+        activity.completeFinishing("test");
     }
 
     /**
@@ -955,13 +996,14 @@
      */
     @Test
     public void testCompleteFinishing_pausing() {
-        mActivity.setState(PAUSING, "test");
-        mActivity.finishing = true;
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(PAUSING, "test");
+        activity.finishing = true;
 
         assertEquals("Activity must not be removed immediately - waiting for paused",
-                mActivity, mActivity.completeFinishing("test"));
-        assertEquals(PAUSING, mActivity.getState());
-        verify(mActivity, never()).destroyIfPossible(anyString());
+                activity, activity.completeFinishing("test"));
+        assertEquals(PAUSING, activity.getState());
+        verify(activity, never()).destroyIfPossible(anyString());
     }
 
     /**
@@ -973,7 +1015,9 @@
      */
     @Test
     public void testCompleteFinishing_keepStateOfNextInvisible() {
-        final ActivityRecord currentTop = mActivity;
+        final ActivityRecord currentTop = createActivityWithTask();
+        final Task task = currentTop.getTask();
+
         currentTop.mVisibleRequested = currentTop.nowVisible = true;
 
         // Simulates that {@code currentTop} starts an existing activity from background (so its
@@ -982,7 +1026,7 @@
         final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity();
         nextTop.setState(STOPPED, "test");
 
-        mStack.mPausingActivity = currentTop;
+        task.mPausingActivity = currentTop;
         currentTop.finishing = true;
         currentTop.setState(PAUSED, "test");
         currentTop.completeFinishing("completePauseLocked");
@@ -999,16 +1043,18 @@
      */
     @Test
     public void testCompleteFinishing_waitForNextVisible() {
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as not visible, so that we will wait for it before removing
         // the top one.
-        mActivity.mVisibleRequested = false;
-        mActivity.nowVisible = false;
-        mActivity.setState(STOPPED, "test");
+        activity.mVisibleRequested = false;
+        activity.nowVisible = false;
+        activity.setState(STOPPED, "test");
 
         assertEquals("Activity must not be removed immediately - waiting for next visible",
                 topActivity, topActivity.completeFinishing("test"));
@@ -1025,23 +1071,24 @@
      */
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_sleeping() {
+        final ActivityRecord activity = createActivityWithTask();
         // Create a top activity on a new task
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord topActivity = createActivityWithTask();
         mDisplayContent.setIsSleeping(true);
-        doReturn(true).when(mActivity).shouldBeVisible();
+        doReturn(true).when(activity).shouldBeVisible();
         topActivity.mVisibleRequested = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
         topActivity.setState(STOPPED, "true");
 
         // Mark the activity behind (on a separate task) as not visible
-        mActivity.mVisibleRequested = false;
-        mActivity.nowVisible = false;
-        mActivity.setState(STOPPED, "test");
+        activity.mVisibleRequested = false;
+        activity.nowVisible = false;
+        activity.setState(STOPPED, "test");
 
-        clearInvocations(mActivity);
+        clearInvocations(activity);
         topActivity.completeFinishing("test");
-        verify(mActivity).setState(eq(RESUMED), any());
+        verify(activity).setState(eq(RESUMED), any());
         verify(topActivity).destroyIfPossible(anyString());
     }
 
@@ -1050,16 +1097,18 @@
      */
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
         topActivity.setState(STOPPED, "true");
         // Mark the bottom activity as not visible, so that we would wait for it before removing
         // the top one.
-        mActivity.mVisibleRequested = false;
-        mActivity.nowVisible = false;
-        mActivity.setState(STOPPED, "test");
+        activity.mVisibleRequested = false;
+        activity.nowVisible = false;
+        activity.setState(STOPPED, "test");
 
         topActivity.completeFinishing("test");
 
@@ -1072,15 +1121,17 @@
      */
     @Test
     public void testCompleteFinishing_waitForIdle() {
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
-        mActivity.nowVisible = true;
-        mActivity.setState(RESUMED, "test");
+        activity.mVisibleRequested = true;
+        activity.nowVisible = true;
+        activity.setState(RESUMED, "test");
 
         topActivity.completeFinishing("test");
 
@@ -1093,15 +1144,17 @@
      */
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_stopped() {
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
         topActivity.setState(STOPPED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
-        mActivity.nowVisible = true;
-        mActivity.setState(RESUMED, "test");
+        activity.mVisibleRequested = true;
+        activity.nowVisible = true;
+        activity.setState(RESUMED, "test");
 
         topActivity.completeFinishing("test");
 
@@ -1114,15 +1167,17 @@
      */
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(activity.getTask()).build();
         topActivity.mVisibleRequested = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
-        mActivity.nowVisible = true;
-        mActivity.setState(RESUMED, "test");
+        activity.mVisibleRequested = true;
+        activity.nowVisible = true;
+        activity.setState(RESUMED, "test");
 
         // Add another stack to become focused and make the activity there visible. This way it
         // simulates finishing in non-focused stack in split-screen.
@@ -1144,10 +1199,12 @@
      */
     @Test
     public void testCompleteFinishing_showWhenLocked() {
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
         // Make keyguard locked and set the top activity show-when-locked.
-        KeyguardController keyguardController = mActivity.mTaskSupervisor.getKeyguardController();
+        KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
         doReturn(true).when(keyguardController).isKeyguardLocked();
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.mVisibleRequested = true;
         topActivity.nowVisible = true;
         topActivity.setState(RESUMED, "true");
@@ -1158,7 +1215,7 @@
         topActivity.setShowWhenLocked(true);
 
         // Verify the stack-top activity is occluded keyguard.
-        assertEquals(topActivity, mStack.topRunningActivity());
+        assertEquals(topActivity, task.topRunningActivity());
         assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
 
         // Finish the top activity
@@ -1167,7 +1224,7 @@
         topActivity.completeFinishing("test");
 
         // Verify new top activity does not occlude keyguard.
-        assertEquals(mActivity, mStack.topRunningActivity());
+        assertEquals(activity, task.topRunningActivity());
         assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
     }
 
@@ -1177,18 +1234,19 @@
      */
     @Test
     public void testCompleteFinishing_ensureActivitiesVisible() {
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
         firstActivity.mVisibleRequested = false;
         firstActivity.nowVisible = false;
         firstActivity.setState(STOPPED, "true");
 
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
         secondActivity.mVisibleRequested = true;
         secondActivity.nowVisible = true;
         secondActivity.setState(PAUSED, "true");
 
-        final ActivityRecord translucentActivity =
-                new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
         translucentActivity.mVisibleRequested = true;
         translucentActivity.nowVisible = true;
         translucentActivity.setState(RESUMED, "true");
@@ -1216,13 +1274,13 @@
      */
     @Test
     public void testDestroyIfPossible() {
+        final ActivityRecord activity = createActivityWithTask();
         doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
-        spyOn(mStack);
-        mActivity.destroyIfPossible("test");
+        activity.destroyIfPossible("test");
 
-        assertEquals(DESTROYING, mActivity.getState());
-        assertTrue(mActivity.finishing);
-        verify(mActivity).destroyImmediately(anyString());
+        assertEquals(DESTROYING, activity.getState());
+        assertTrue(activity.finishing);
+        verify(activity).destroyImmediately(anyString());
     }
 
     /**
@@ -1232,23 +1290,23 @@
      */
     @Test
     public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
+        final ActivityRecord activity = createActivityWithTask();
         // Empty the home stack.
-        final Task homeStack = mActivity.getDisplayArea().getRootHomeTask();
+        final Task homeStack = activity.getDisplayArea().getRootHomeTask();
         homeStack.forAllLeafTasks((t) -> {
             homeStack.removeChild(t, "test");
         }, true /* traverseTopToBottom */);
-        mActivity.finishing = true;
+        activity.finishing = true;
         doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
-        spyOn(mStack);
 
         // Try to destroy the last activity above the home stack.
-        mActivity.destroyIfPossible("test");
+        activity.destroyIfPossible("test");
 
         // Verify that the activity was not actually destroyed, but waits for next one to come up
         // instead.
-        verify(mActivity, never()).destroyImmediately(anyString());
-        assertEquals(FINISHING, mActivity.getState());
-        assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity));
+        verify(activity, never()).destroyImmediately(anyString());
+        assertEquals(FINISHING, activity.getState());
+        assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity));
     }
 
     /**
@@ -1258,22 +1316,23 @@
      */
     @Test
     public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
+        final ActivityRecord activity = createActivityWithTask();
         // Empty the home root task.
-        final Task homeRootTask = mActivity.getDisplayArea().getRootHomeTask();
+        final Task homeRootTask = activity.getDisplayArea().getRootHomeTask();
         homeRootTask.forAllLeafTasks((t) -> {
             homeRootTask.removeChild(t, "test");
         }, true /* traverseTopToBottom */);
-        mActivity.finishing = true;
-        mActivity.mVisibleRequested = true;
-        spyOn(mStack);
+        activity.setState(STARTED, "test");
+        activity.finishing = true;
+        activity.mVisibleRequested = true;
 
         // Try to finish the last activity above the home stack.
-        mActivity.completeFinishing("test");
+        activity.completeFinishing("test");
 
         // Verify that the activity is not destroyed immediately, but waits for next one to come up.
-        verify(mActivity, never()).destroyImmediately(anyString());
-        assertEquals(FINISHING, mActivity.getState());
-        assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity));
+        verify(activity, never()).destroyImmediately(anyString());
+        assertEquals(FINISHING, activity.getState());
+        assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity));
     }
 
     /**
@@ -1282,10 +1341,11 @@
      */
     @Test
     public void testDestroyImmediately_hadApp_finishing() {
-        mActivity.finishing = true;
-        mActivity.destroyImmediately("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = true;
+        activity.destroyImmediately("test");
 
-        assertEquals(DESTROYING, mActivity.getState());
+        assertEquals(DESTROYING, activity.getState());
     }
 
     /**
@@ -1294,10 +1354,11 @@
      */
     @Test
     public void testDestroyImmediately_hadApp_notFinishing() {
-        mActivity.finishing = false;
-        mActivity.destroyImmediately("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.finishing = false;
+        activity.destroyImmediately("test");
 
-        assertEquals(DESTROYED, mActivity.getState());
+        assertEquals(DESTROYED, activity.getState());
     }
 
     /**
@@ -1306,14 +1367,15 @@
      */
     @Test
     public void testDestroyImmediately_noApp_finishing() {
-        mActivity.app = null;
-        mActivity.finishing = true;
-        final Task task = mActivity.getTask();
+        final ActivityRecord activity = createActivityWithTask();
+        activity.app = null;
+        activity.finishing = true;
+        final Task task = activity.getTask();
 
-        mActivity.destroyImmediately("test");
+        activity.destroyImmediately("test");
 
-        assertEquals(DESTROYED, mActivity.getState());
-        assertNull(mActivity.getTask());
+        assertEquals(DESTROYED, activity.getState());
+        assertNull(activity.getTask());
         assertEquals(0, task.getChildCount());
     }
 
@@ -1323,14 +1385,15 @@
      */
     @Test
     public void testDestroyImmediately_noApp_notFinishing() {
-        mActivity.app = null;
-        mActivity.finishing = false;
-        final Task task = mActivity.getTask();
+        final ActivityRecord activity = createActivityWithTask();
+        activity.app = null;
+        activity.finishing = false;
+        final Task task = activity.getTask();
 
-        mActivity.destroyImmediately("test");
+        activity.destroyImmediately("test");
 
-        assertEquals(DESTROYED, mActivity.getState());
-        assertEquals(task, mActivity.getTask());
+        assertEquals(DESTROYED, activity.getState());
+        assertEquals(task, activity.getTask());
         assertEquals(1, task.getChildCount());
     }
 
@@ -1339,11 +1402,12 @@
      */
     @Test
     public void testSafelyDestroy_nonDestroyable() {
-        doReturn(false).when(mActivity).isDestroyable();
+        final ActivityRecord activity = createActivityWithTask();
+        doReturn(false).when(activity).isDestroyable();
 
-        mActivity.safelyDestroy("test");
+        activity.safelyDestroy("test");
 
-        verify(mActivity, never()).destroyImmediately(anyString());
+        verify(activity, never()).destroyImmediately(anyString());
     }
 
     /**
@@ -1351,29 +1415,31 @@
      */
     @Test
     public void testSafelyDestroy_destroyable() {
-        doReturn(true).when(mActivity).isDestroyable();
+        final ActivityRecord activity = createActivityWithTask();
+        doReturn(true).when(activity).isDestroyable();
 
-        mActivity.safelyDestroy("test");
+        activity.safelyDestroy("test");
 
-        verify(mActivity).destroyImmediately(anyString());
+        verify(activity).destroyImmediately(anyString());
     }
 
     @Test
     public void testRemoveFromHistory() {
-        final Task stack = mActivity.getRootTask();
-        final Task task = mActivity.getTask();
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final Task rootTask = activity.getRootTask();
+        final Task task = activity.getTask();
+        final WindowProcessController wpc = activity.app;
         assertTrue(wpc.hasActivities());
 
-        mActivity.removeFromHistory("test");
+        activity.removeFromHistory("test");
 
-        assertEquals(DESTROYED, mActivity.getState());
-        assertNull(mActivity.app);
-        assertNull(mActivity.getTask());
+        assertEquals(DESTROYED, activity.getState());
+        assertNull(activity.app);
+        assertNull(activity.getTask());
         assertFalse(wpc.hasActivities());
         assertEquals(0, task.getChildCount());
         assertEquals(task.getRootTask(), task);
-        assertEquals(0, stack.getChildCount());
+        assertEquals(0, rootTask.getChildCount());
     }
 
     /**
@@ -1382,8 +1448,9 @@
      */
     @Test(expected = IllegalStateException.class)
     public void testDestroyed_notDestroying() {
-        mActivity.setState(STOPPED, "test");
-        mActivity.destroyed("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(STOPPED, "test");
+        activity.destroyed("test");
     }
 
     /**
@@ -1391,10 +1458,11 @@
      */
     @Test
     public void testDestroyed_destroying() {
-        mActivity.setState(DESTROYING, "test");
-        mActivity.destroyed("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(DESTROYING, "test");
+        activity.destroyed("test");
 
-        verify(mActivity).removeFromHistory(anyString());
+        verify(activity).removeFromHistory(anyString());
     }
 
     /**
@@ -1402,15 +1470,17 @@
      */
     @Test
     public void testDestroyed_destroyed() {
-        mActivity.setState(DESTROYED, "test");
-        mActivity.destroyed("test");
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(DESTROYED, "test");
+        activity.destroyed("test");
 
-        verify(mActivity).removeFromHistory(anyString());
+        verify(activity).removeFromHistory(anyString());
     }
 
     @Test
     public void testActivityOverridesProcessConfig() {
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertFalse(wpc.registeredForDisplayConfigChanges());
 
@@ -1418,18 +1488,19 @@
                 createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
 
         assertTrue(wpc.registeredForActivityConfigChanges());
-        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+        assertEquals(0, activity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
-        assertNotEquals(mActivity.getConfiguration(),
+        assertNotEquals(activity.getConfiguration(),
                 secondaryDisplayActivity.getConfiguration());
     }
 
     @Test
     public void testActivityOverridesProcessConfig_TwoActivities() {
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
         assertTrue(wpc.registeredForActivityConfigChanges());
 
-        final Task firstTaskRecord = mActivity.getTask();
+        final Task firstTaskRecord = activity.getTask();
         final ActivityRecord secondActivityRecord =
                 new ActivityBuilder(mAtm).setTask(firstTaskRecord).setUseProcess(wpc).build();
 
@@ -1440,11 +1511,12 @@
 
     @Test
     public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() {
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
         assertTrue(wpc.registeredForActivityConfigChanges());
 
         final ActivityRecord secondActivityRecord =
-                new ActivityBuilder(mAtm).setTask(mTask).setUseProcess(wpc).build();
+                new ActivityBuilder(mAtm).setTask(activity.getTask()).setUseProcess(wpc).build();
 
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
@@ -1453,7 +1525,8 @@
 
     @Test
     public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() {
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
         assertTrue(wpc.registeredForActivityConfigChanges());
 
         final ActivityRecord secondActivityRecord =
@@ -1466,10 +1539,11 @@
 
     @Test
     public void testActivityOnCancelFixedRotationTransform() {
-        final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation();
+        final ActivityRecord activity = createActivityWithTask();
+        final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation();
         spyOn(displayRotation);
 
-        final DisplayContent display = mActivity.mDisplayContent;
+        final DisplayContent display = activity.mDisplayContent;
         final int originalRotation = display.getRotation();
 
         // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
@@ -1477,17 +1551,17 @@
         doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
                 anyInt() /* orientation */, anyInt() /* lastRotation */);
         // Set to visible so the activity can freeze the screen.
-        mActivity.setVisibility(true);
+        activity.setVisibility(true);
 
-        display.rotateInDifferentOrientationIfNeeded(mActivity);
-        display.setFixedRotationLaunchingAppUnchecked(mActivity);
+        display.rotateInDifferentOrientationIfNeeded(activity);
+        display.setFixedRotationLaunchingAppUnchecked(activity);
         displayRotation.updateRotationUnchecked(true /* forceUpdate */);
 
         assertTrue(displayRotation.isRotatingSeamlessly());
 
         // The launching rotated app should not be cleared when waiting for remote rotation.
         display.continueUpdateOrientationForDiffOrienLaunchingApp();
-        assertTrue(display.isFixedRotationLaunchingApp(mActivity));
+        assertTrue(display.isFixedRotationLaunchingApp(activity));
 
         // Simulate the rotation has been updated to previous one, e.g. sensor updates before the
         // remote rotation is completed.
@@ -1495,16 +1569,16 @@
                 anyInt() /* orientation */, anyInt() /* lastRotation */);
         display.updateOrientation();
 
-        final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
-        mActivity.finishFixedRotationTransform();
+        final DisplayInfo rotatedInfo = activity.getFixedRotationTransformDisplayInfo();
+        activity.finishFixedRotationTransform();
         final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
         assertNotNull(rotationAnim);
         rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
 
         // Because the display doesn't rotate, the rotated activity needs to cancel the fixed
         // rotation. There should be a rotation animation to cover the change of activity.
-        verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
-        assertTrue(mActivity.isFreezingScreen());
+        verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation);
+        assertTrue(activity.isFreezingScreen());
         assertFalse(displayRotation.isRotatingSeamlessly());
         assertTrue(rotationAnim.isRotating());
 
@@ -1512,44 +1586,46 @@
         // the rotated activity should also be restored by clearing the transform.
         displayRotation.updateRotationUnchecked(true /* forceUpdate */);
         doReturn(false).when(displayRotation).isWaitingForRemoteRotation();
-        clearInvocations(mActivity);
-        display.setFixedRotationLaunchingAppUnchecked(mActivity);
+        clearInvocations(activity);
+        display.setFixedRotationLaunchingAppUnchecked(activity);
         display.sendNewConfiguration();
 
         assertFalse(display.hasTopFixedRotationLaunchingApp());
-        assertFalse(mActivity.hasFixedRotationTransform());
+        assertFalse(activity.hasFixedRotationTransform());
     }
 
     @Test
     public void testIsSnapshotCompatible() {
+        final ActivityRecord activity = createActivityWithTask();
         final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
-                .setRotation(mActivity.getWindowConfiguration().getRotation())
+                .setRotation(activity.getWindowConfiguration().getRotation())
                 .build();
 
-        assertTrue(mActivity.isSnapshotCompatible(snapshot));
+        assertTrue(activity.isSnapshotCompatible(snapshot));
 
-        setRotatedScreenOrientationSilently(mActivity);
+        setRotatedScreenOrientationSilently(activity);
 
-        assertFalse(mActivity.isSnapshotCompatible(snapshot));
+        assertFalse(activity.isSnapshotCompatible(snapshot));
     }
 
     @Test
     public void testFixedRotationSnapshotStartingWindow() {
+        final ActivityRecord activity = createActivityWithTask();
         // TaskSnapshotSurface requires a fullscreen opaque window.
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
         params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
         final TestWindowState w = new TestWindowState(
-                mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
-        mActivity.addWindow(w);
+                mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity);
+        activity.addWindow(w);
 
         // Assume the activity is launching in different rotation, and there was an available
         // snapshot accepted by {@link Activity#isSnapshotCompatible}.
         final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
-                .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4)
+                .setRotation((activity.getWindowConfiguration().getRotation() + 1) % 4)
                 .build();
-        setRotatedScreenOrientationSilently(mActivity);
-        mActivity.setVisible(false);
+        setRotatedScreenOrientationSilently(activity);
+        activity.setVisible(false);
 
         final IWindowSession session = WindowManagerGlobal.getWindowSession();
         spyOn(session);
@@ -1561,7 +1637,7 @@
                     any() /* requestedVisibility */, any() /* outFrame */,
                     any() /* outDisplayCutout */, any() /* outInputChannel */,
                     any() /* outInsetsState */, any() /* outActiveControls */);
-            TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot);
+            TaskSnapshotSurface.create(mAtm.mWindowManager, activity, snapshot);
         } catch (RemoteException ignored) {
         } finally {
             reset(session);
@@ -1570,8 +1646,8 @@
         // Because the rotation of snapshot and the corresponding top activity are different, fixed
         // rotation should be applied when creating snapshot surface if the display rotation may be
         // changed according to the activity orientation.
-        assertTrue(mActivity.hasFixedRotationTransform());
-        assertTrue(mActivity.mDisplayContent.isFixedRotationLaunchingApp(mActivity));
+        assertTrue(activity.hasFixedRotationTransform());
+        assertTrue(activity.mDisplayContent.isFixedRotationLaunchingApp(activity));
     }
 
     /**
@@ -1604,11 +1680,12 @@
 
     @Test
     public void testActivityReparentChangesProcessOverride() {
-        final WindowProcessController wpc = mActivity.app;
-        final Task initialTask = mActivity.getTask();
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
+        final Task initialTask = activity.getTask();
         final Configuration initialConf =
-                new Configuration(mActivity.getMergedOverrideConfiguration());
-        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+                new Configuration(activity.getMergedOverrideConfiguration());
+        assertEquals(0, activity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
         assertTrue(wpc.registeredForActivityConfigChanges());
 
@@ -1621,22 +1698,23 @@
         assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi);
 
         // Reparent the activity and verify that config override changed.
-        mActivity.reparent(newTask, 0 /* top */, "test");
-        assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi);
-        assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
+        activity.reparent(newTask, 0 /* top */, "test");
+        assertEquals(activity.getConfiguration().densityDpi, newConfig.densityDpi);
+        assertEquals(activity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
 
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
-        assertEquals(0, mActivity.getMergedOverrideConfiguration()
+        assertEquals(0, activity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
     }
 
     @Test
     public void testActivityReparentDoesntClearProcessOverride_TwoActivities() {
-        final WindowProcessController wpc = mActivity.app;
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
         final Configuration initialConf =
-                new Configuration(mActivity.getMergedOverrideConfiguration());
-        final Task initialTask = mActivity.getTask();
+                new Configuration(activity.getMergedOverrideConfiguration());
+        final Task initialTask = activity.getTask();
         final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(initialTask)
                 .setUseProcess(wpc).build();
 
@@ -1660,7 +1738,7 @@
         assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
 
         // Reparent the first activity and verify that config override didn't change.
-        mActivity.reparent(newTask, 1 /* top */, "test");
+        activity.reparent(newTask, 1 /* top */, "test");
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertEquals(0, secondActivity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
@@ -1703,67 +1781,90 @@
 
     @Test
     public void testFullscreenWindowCanTurnScreenOn() {
-        mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        doReturn(true).when(mActivity).getTurnScreenOnFlag();
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        doReturn(true).when(activity).getTurnScreenOnFlag();
 
-        assertTrue(mActivity.canTurnScreenOn());
+        assertTrue(activity.canTurnScreenOn());
     }
 
     @Test
     public void testFreeformWindowCanTurnScreenOn() {
-        mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        doReturn(true).when(mActivity).getTurnScreenOnFlag();
+        final ActivityRecord activity = createActivityWithTask();
+        final Task task = activity.getTask();
+        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        doReturn(true).when(activity).getTurnScreenOnFlag();
 
-        assertTrue(mActivity.canTurnScreenOn());
+        assertTrue(activity.canTurnScreenOn());
     }
 
     @Test
     public void testGetLockTaskLaunchMode() {
+        final ActivityRecord activity = createActivityWithTask();
         final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true);
-        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
         assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED,
-                ActivityRecord.getLockTaskLaunchMode(mActivity.info, options));
+                ActivityRecord.getLockTaskLaunchMode(activity.info, options));
 
-        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+        activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
         assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
-                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+                ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
 
-        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+        activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
         assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
-                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+                ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
 
-        mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+        activity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
         assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS,
-                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+                ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
 
-        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+        activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
         assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER,
-                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+                ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
 
     }
 
     @Test
     public void testProcessInfoUpdateWhenSetState() {
-        spyOn(mActivity.app);
-        verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */);
-        verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */);
-        verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */);
-        verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */);
+        final ActivityRecord activity = createActivityWithTask();
+        activity.setState(INITIALIZING, "test");
+        spyOn(activity.app);
+        verifyProcessInfoUpdate(activity, RESUMED,
+                true /* shouldUpdate */, true /* activityChange */);
+        verifyProcessInfoUpdate(activity, PAUSED,
+                false /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(activity, STOPPED,
+                false /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(activity, STARTED,
+                true /* shouldUpdate */, true /* activityChange */);
 
-        mActivity.app.removeActivity(mActivity, true /* keepAssociation */);
-        verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */);
-        verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */);
+        activity.app.removeActivity(activity, true /* keepAssociation */);
+        verifyProcessInfoUpdate(activity, DESTROYING,
+                true /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(activity, DESTROYED,
+                true /* shouldUpdate */, false /* activityChange */);
     }
 
-    private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate,
-            boolean activityChange) {
-        reset(mActivity.app);
-        mActivity.setState(state, "test");
-        verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(),
+    private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+            boolean shouldUpdate, boolean activityChange) {
+        reset(activity.app);
+        activity.setState(state, "test");
+        verify(activity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(),
                 eq(activityChange), anyBoolean(), anyBoolean());
     }
 
+    private ActivityRecord createActivityWithTask() {
+        return new ActivityBuilder(mAtm).setCreateTask(true).setOnTop(true).build();
+    }
+
+    private ActivityRecord createActivityWith2LevelTask() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateParentTask(true).setCreateActivity(true).build();
+        return task.getTopNonFinishingActivity();
+    }
+
     /**
      * Creates an activity on display. For non-default display request it will also create a new
      * display with custom DisplayInfo.
@@ -1777,9 +1878,7 @@
             display = new TestDisplayContent.Builder(mAtm, 2000, 1000).setDensityDpi(300)
                     .setPosition(DisplayContent.POSITION_TOP).build();
         }
-        final Task stack = display.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
         return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index d872eb8..8ccbb8f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -43,6 +43,7 @@
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
 import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
 import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
@@ -91,59 +92,58 @@
 @RunWith(WindowTestRunner.class)
 public class ActivityStackTests extends WindowTestsBase {
     private TaskDisplayArea mDefaultTaskDisplayArea;
-    private Task mStack;
-    private Task mTask;
 
     @Before
     public void setUp() throws Exception {
         mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        spyOn(mStack);
-        mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build();
     }
 
     @Test
     public void testResumedActivity() {
-        final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
-        assertNull(mStack.getResumedActivity());
+        final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = r.getTask();
+        assertNull(task.getResumedActivity());
         r.setState(RESUMED, "testResumedActivity");
-        assertEquals(r, mStack.getResumedActivity());
+        assertEquals(r, task.getResumedActivity());
         r.setState(PAUSING, "testResumedActivity");
-        assertNull(mStack.getResumedActivity());
+        assertNull(task.getResumedActivity());
     }
 
     @Test
     public void testResumedActivityFromTaskReparenting() {
-        final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        final ActivityRecord r = new ActivityBuilder(mAtm)
+                .setCreateTask(true).setParentTask(parentTask).build();
+        final Task task = r.getTask();
         // Ensure moving task between two stacks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
-        assertEquals(r, mStack.getResumedActivity());
+        assertEquals(r, parentTask.getResumedActivity());
 
-        final Task destStack = mDefaultTaskDisplayArea.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+        final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
                 false /* animate */, true /* deferResume*/,
                 "testResumedActivityFromTaskReparenting");
 
-        assertNull(mStack.getResumedActivity());
+        assertNull(parentTask.getResumedActivity());
         assertEquals(r, destStack.getResumedActivity());
     }
 
     @Test
     public void testResumedActivityFromActivityReparenting() {
-        final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        final ActivityRecord r = new ActivityBuilder(mAtm)
+                .setCreateTask(true).setParentTask(parentTask).build();
+        final Task task = r.getTask();
         // Ensure moving task between two stacks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
-        assertEquals(r, mStack.getResumedActivity());
+        assertEquals(r, parentTask.getResumedActivity());
 
-        final Task destStack = mDefaultTaskDisplayArea.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false, false,
+        final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
+        task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+                false /* animate */, false /* deferResume*/,
                 "testResumedActivityFromActivityReparenting");
 
-        assertNull(mStack.getResumedActivity());
+        assertNull(parentTask.getResumedActivity());
         assertEquals(r, destStack.getResumedActivity());
     }
 
@@ -292,7 +292,7 @@
     public void testStopActivityWhenActivityDestroyed() {
         final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
-        mStack.moveToFront("testStopActivityWithDestroy");
+        r.getTask().moveToFront("testStopActivityWithDestroy");
         r.stopIfPossible();
         // Mostly testing to make sure there is a crash in the call part, so if we get here we are
         // good-to-go!
@@ -327,7 +327,8 @@
                 targetActivity);
         final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
                 aliasActivity);
-        final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
+        final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
+        final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build();
         task.origActivity = alias;
         task.realActivity = target;
         new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity(
@@ -337,14 +338,14 @@
         final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
                 target).setTargetActivity(targetActivity).build();
         RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
-        result.process(r1, mStack);
+        result.process(r1, parentTask);
         assertThat(result.mRecord).isNotNull();
 
         // Using alias activity to find task.
         final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
                 alias).setTargetActivity(targetActivity).build();
         result = new RootWindowContainer.FindTaskResult();
-        result.process(r2, mStack);
+        result.process(r2, parentTask);
         assertThat(result.mRecord).isNotNull();
     }
 
@@ -735,8 +736,6 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task fullscreenStack = createStackForShouldBeVisibleTest(
@@ -755,8 +754,6 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task fullscreenStack = createStackForShouldBeVisibleTest(
@@ -775,8 +772,6 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task fullscreenStack = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
@@ -795,8 +790,6 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
@@ -822,8 +815,6 @@
     @Test
     public void
             testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
@@ -846,8 +837,6 @@
 
     @Test
     public void testMoveHomeStackBehindStack_BehindHomeStack() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
@@ -869,8 +858,6 @@
 
     @Test
     public void testMoveHomeStackBehindStack() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
-
         final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
@@ -1019,11 +1006,12 @@
 
     @Test
     public void testFinishDisabledPackageActivities_FinishAliveActivities() {
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
         firstActivity.setState(STOPPED, "testFinishDisabledPackageActivities");
         secondActivity.setState(RESUMED, "testFinishDisabledPackageActivities");
-        mStack.mResumedActivity = secondActivity;
+        task.mResumedActivity = secondActivity;
 
         // Note the activities have non-null ActivityRecord.app, so it won't remove directly.
         mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
@@ -1039,10 +1027,11 @@
 
     @Test
     public void testFinishDisabledPackageActivities_RemoveNonAliveActivities() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         // The overlay activity is not in the disabled package but it is in the same task.
-        final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(mTask)
+        final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task)
                 .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
         // If the task only remains overlay activity, the task should also be removed.
         // See {@link ActivityStack#removeFromHistory}.
@@ -1053,7 +1042,7 @@
         activity.app = null;
         overlayActivity.app = null;
 
-        assertEquals(2, mTask.getChildCount());
+        assertEquals(2, task.getChildCount());
 
         mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
                 activity.packageName, null  /* filterByClasses */, true /* doit */,
@@ -1062,14 +1051,14 @@
         // Although the overlay activity is in another package, the non-overlay activities are
         // removed from the task. Since the overlay activity should be removed as well, the task
         // should be empty.
-        assertFalse(mTask.hasChild());
-        assertFalse(mStack.hasChild());
+        assertFalse(task.hasChild());
     }
 
     @Test
     public void testHandleAppDied() {
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
 
         // Making the first activity a task overlay means it will be removed from the task's
         // activities as well once second activity is removed as handleAppDied processes the
@@ -1080,17 +1069,17 @@
         // second activity will be immediately removed as it has no state.
         secondActivity.setSavedState(null /* savedState */);
 
-        assertEquals(2, mTask.getChildCount());
+        assertEquals(2, task.getChildCount());
 
         secondActivity.app.handleAppDied();
 
-        assertFalse(mTask.hasChild());
-        assertFalse(mStack.hasChild());
+        assertFalse(task.hasChild());
     }
 
     @Test
     public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
         activity.launchCount = 1;
@@ -1098,13 +1087,13 @@
 
         activity.app.handleAppDied();
 
-        assertEquals(1, mTask.getChildCount());
-        assertEquals(1, mStack.getChildCount());
+        assertEquals(1, task.getChildCount());
     }
 
     @Test
     public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
         activity.launchCount = 3;
@@ -1112,13 +1101,13 @@
 
         activity.app.handleAppDied();
 
-        assertFalse(mTask.hasChild());
-        assertFalse(mStack.hasChild());
+        assertFalse(task.hasChild());
     }
 
     @Test
     public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
         activity.launchCount = 1;
@@ -1126,13 +1115,13 @@
 
         activity.app.handleAppDied();
 
-        assertEquals(1, mTask.getChildCount());
-        assertEquals(1, mStack.getChildCount());
+        assertEquals(1, task.getChildCount());
     }
 
     @Test
     public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
         activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
         activity.launchCount = 3;
@@ -1140,22 +1129,22 @@
 
         activity.app.handleAppDied();
 
-        assertFalse(mTask.hasChild());
-        assertFalse(mStack.hasChild());
+        assertFalse(task.hasChild());
     }
 
     @Test
     public void testCompletePauseOnResumeWhilePausingActivity() {
-        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
         doReturn(true).when(bottomActivity).attachedToProcess();
-        mStack.mPausingActivity = null;
-        mStack.mResumedActivity = bottomActivity;
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        task.mPausingActivity = null;
+        task.mResumedActivity = bottomActivity;
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
 
-        mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
+        task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
                 "test");
-        verify(mStack).completePauseLocked(anyBoolean(), eq(topActivity));
+        verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
     }
 
     @Test
@@ -1234,10 +1223,11 @@
 
     @Test
     public void testStackOrderChangedOnRemoveStack() {
+        final Task task = new TaskBuilder(mSupervisor).build();
         StackOrderChangedListener listener = new StackOrderChangedListener();
         mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
         try {
-            mDefaultTaskDisplayArea.removeStack(mStack);
+            mDefaultTaskDisplayArea.removeStack(task);
         } finally {
             mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
         }
@@ -1246,13 +1236,14 @@
 
     @Test
     public void testStackOrderChangedOnAddPositionStack() {
-        mDefaultTaskDisplayArea.removeStack(mStack);
+        final Task task = new TaskBuilder(mSupervisor).build();
+        mDefaultTaskDisplayArea.removeStack(task);
 
         StackOrderChangedListener listener = new StackOrderChangedListener();
         mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
         try {
-            mStack.mReparenting = true;
-            mDefaultTaskDisplayArea.addChild(mStack, 0);
+            task.mReparenting = true;
+            mDefaultTaskDisplayArea.addChild(task, 0);
         } finally {
             mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
         }
@@ -1284,20 +1275,21 @@
         spyOn(starter);
         doReturn(ActivityManager.START_SUCCESS).when(starter).execute();
 
-        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask)
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task)
                 .setUid(firstActivity.getUid() + 1).build();
         doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString());
 
         final IApplicationThread thread = secondActivity.app.getThread();
         secondActivity.app.setThread(null);
         // This should do nothing from a non-attached caller.
-        assertFalse(mStack.navigateUpTo(secondActivity /* source record */,
+        assertFalse(task.navigateUpTo(secondActivity /* source record */,
                 firstActivity.intent /* destIntent */, null /* destGrants */,
                 0 /* resultCode */, null /* resultData */, null /* resultGrants */));
 
         secondActivity.app.setThread(thread);
-        assertTrue(mStack.navigateUpTo(secondActivity /* source record */,
+        assertTrue(task.navigateUpTo(secondActivity /* source record */,
                 firstActivity.intent /* destIntent */, null /* destGrants */,
                 0 /* resultCode */, null /* resultData */, null /* resultGrants */));
         // The firstActivity uses default launch mode, so the activities between it and itself will
@@ -1313,9 +1305,10 @@
         final String affinity = "affinity";
         final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity)
                 .setUid(Binder.getCallingUid()).setCreateTask(true).build();
-        activity.getTask().affinity = activity.taskAffinity;
+        final Task task = activity.getTask();
+        task.affinity = activity.taskAffinity;
 
-        assertFalse(mStack.shouldUpRecreateTaskLocked(activity, affinity));
+        assertFalse(task.shouldUpRecreateTaskLocked(activity, affinity));
     }
 
     @Test
@@ -1323,21 +1316,23 @@
         final String affinity = "affinity";
         final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity)
                 .setUid(Binder.getCallingUid()).setCreateTask(true).build();
-        activity.getTask().affinity = activity.taskAffinity;
+        final Task task = activity.getTask();
+        task.affinity = activity.taskAffinity;
         final String fakeAffinity = activity.getUid() + activity.taskAffinity;
 
-        assertTrue(mStack.shouldUpRecreateTaskLocked(activity, fakeAffinity));
+        assertTrue(task.shouldUpRecreateTaskLocked(activity, fakeAffinity));
     }
 
     @Test
     public void testResetTaskWithFinishingActivities() {
-        final ActivityRecord taskTop = new ActivityBuilder(mAtm).setTask(mStack).build();
+        final ActivityRecord taskTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = taskTop.getTask();
         // Make all activities in the task are finishing to simulate Task#getTopActivity
         // returns null.
         taskTop.finishing = true;
 
         final ActivityRecord newR = new ActivityBuilder(mAtm).build();
-        final ActivityRecord result = mStack.resetTaskIfNeeded(taskTop, newR);
+        final ActivityRecord result = task.resetTaskIfNeeded(taskTop, newR);
         assertThat(result).isEqualTo(taskTop);
     }
 
@@ -1345,14 +1340,15 @@
     public void testIterateOccludedActivity() {
         final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
         final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
-        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         // Top activity occludes bottom activity.
-        doReturn(true).when(mStack).shouldBeVisible(any());
+        doReturn(true).when(task).shouldBeVisible(any());
         assertTrue(topActivity.shouldBeVisible());
         assertFalse(bottomActivity.shouldBeVisible());
 
-        mStack.forAllOccludedActivities(handleOccludedActivity);
+        task.forAllOccludedActivities(handleOccludedActivity);
         assertThat(occludedActivities).containsExactly(bottomActivity);
 
         // Top activity doesn't occlude parent, so the bottom activity is not occluded.
@@ -1360,18 +1356,18 @@
         assertTrue(bottomActivity.shouldBeVisible());
 
         occludedActivities.clear();
-        mStack.forAllOccludedActivities(handleOccludedActivity);
+        task.forAllOccludedActivities(handleOccludedActivity);
         assertThat(occludedActivities).isEmpty();
 
         // A finishing activity should not occlude other activities behind.
-        final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+        final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
         finishingActivity.finishing = true;
         doCallRealMethod().when(finishingActivity).occludesParent();
         assertTrue(topActivity.shouldBeVisible());
         assertTrue(bottomActivity.shouldBeVisible());
 
         occludedActivities.clear();
-        mStack.forAllOccludedActivities(handleOccludedActivity);
+        task.forAllOccludedActivities(handleOccludedActivity);
         assertThat(occludedActivities).isEmpty();
     }
 
@@ -1385,8 +1381,9 @@
         // Start 2 activities that their processes have not yet started.
         final ActivityRecord[] activities = new ActivityRecord[2];
         mSupervisor.beginDeferResume();
+        final Task task = new TaskBuilder(mSupervisor).build();
         for (int i = 0; i < activities.length; i++) {
-            final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+            final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
             activities[i] = r;
             doReturn(null).when(mAtm).getProcessController(
                     eq(r.processName), eq(r.info.applicationInfo.uid));
@@ -1405,7 +1402,7 @@
         // Assume the top activity is going to resume and
         // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown
         // visibility records that are occluded.
-        mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+        task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
         // Assume the top activity relayouted, just remove it directly.
         unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
         // All unresolved records should be removed.
@@ -1414,15 +1411,16 @@
 
     @Test
     public void testNonTopVisibleActivityNotResume() {
+        final Task task = new TaskBuilder(mSupervisor).build();
         final ActivityRecord nonTopVisibleActivity =
-                new ActivityBuilder(mAtm).setTask(mTask).build();
-        new ActivityBuilder(mAtm).setTask(mTask).build();
+                new ActivityBuilder(mAtm).setTask(task).build();
+        new ActivityBuilder(mAtm).setTask(task).build();
         doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
         doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
         doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
                 anyBoolean());
 
-        mStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+        task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */);
         verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
                 anyBoolean());
@@ -1436,16 +1434,17 @@
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
             boolean expected) {
+        final Task task = new TaskBuilder(mSupervisor).build();
         final DisplayContent display = mock(DisplayContent.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
         display.isDefaultDisplay = isDefaultDisplay;
 
-        mStack.mDisplayContent = display;
+        task.mDisplayContent = display;
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
+        doReturn(focusedStack).when(task).isFocusedStackOnDisplay();
 
-        assertEquals(expected, mStack.shouldSleepActivities());
+        assertEquals(expected, task.shouldSleepActivities());
     }
 
     private static class StackOrderChangedListener
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f607448..bded3f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -126,6 +126,7 @@
     private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
     private static final int UNIMPORTANT_UID = 12345;
     private static final int UNIMPORTANT_UID2 = 12346;
+    private static final int CURRENT_IME_UID = 12347;
 
     @Before
     public void setUp() throws Exception {
@@ -315,6 +316,12 @@
         return prepareStarter(launchFlags, mockGetLaunchStack, LAUNCH_MULTIPLE);
     }
 
+    private void setupImeWindow() {
+        final WindowState imeWindow = createWindow(null, W_INPUT_METHOD,
+                "mImeWindow", CURRENT_IME_UID);
+        mDisplayContent.mInputMethodWindow = imeWindow;
+    }
+
     /**
      * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
      *
@@ -654,6 +661,14 @@
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
                 false, false, false, false, true);
+
+        setupImeWindow();
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callingPackageNameIsIme_notAborted", false,
+                CURRENT_IME_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false, false, false, false);
+
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
@@ -680,14 +695,11 @@
             boolean isCallingUidDeviceOwner,
             boolean isPinnedSingleInstance) {
         // window visibility
-        doReturn(callingUidHasVisibleWindow).when(mAtm.mWindowManager.mRoot)
-                .isAnyNonToastWindowVisibleForUid(callingUid);
-        doReturn(realCallingUidHasVisibleWindow).when(mAtm.mWindowManager.mRoot)
-                .isAnyNonToastWindowVisibleForUid(realCallingUid);
-
+        doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid);
+        doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid);
         // process importance
-        doReturn(callingUidProcState).when(mAtm).getUidState(callingUid);
-        doReturn(realCallingUidProcState).when(mAtm).getUidState(realCallingUid);
+        mAtm.mActiveUids.onUidActive(callingUid, callingUidProcState);
+        mAtm.mActiveUids.onUidActive(realCallingUid, realCallingUidProcState);
         // foreground activities
         final IApplicationThread caller = mock(IApplicationThread.class);
         final WindowProcessListener listener = mock(WindowProcessListener.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index f61253c..017ed88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -19,8 +19,6 @@
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -44,7 +42,6 @@
 
 import androidx.test.filters.MediumTest;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -58,13 +55,6 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class ActivityTaskSupervisorTests extends WindowTestsBase {
-    private Task mFullscreenTask;
-
-    @Before
-    public void setUp() throws Exception {
-        mFullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-    }
 
     /**
      * Ensures that an activity is removed from the stopping activities list once it is resumed.
@@ -72,7 +62,7 @@
     @Test
     public void testStoppingActivityRemovedWhenResumed() {
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(mFullscreenTask).build();
+                .setCreateTask(true).build();
         mSupervisor.mStoppingActivities.add(firstActivity);
 
         firstActivity.completeResumeLocked();
@@ -86,7 +76,7 @@
     @Test
     public void testReportWaitingActivityLaunchedIfNeeded() {
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
-                .setTask(mFullscreenTask).build();
+                .setCreateTask(true).build();
 
         final WaitResult taskToFrontWait = new WaitResult();
         mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
@@ -153,7 +143,7 @@
     @Test
     public void testNotifyTaskFocusChanged() {
         final ActivityRecord fullScreenActivityA = new ActivityBuilder(mAtm).setCreateTask(true)
-                .setParentTask(mFullscreenTask).build();
+                .build();
         final Task taskA = fullScreenActivityA.getTask();
 
         final TaskChangeNotificationController taskChangeNotifier =
@@ -166,7 +156,7 @@
         reset(taskChangeNotifier);
 
         final ActivityRecord fullScreenActivityB = new ActivityBuilder(mAtm).setCreateTask(true)
-                .setParentTask(mFullscreenTask).build();
+                .build();
         final Task taskB = fullScreenActivityB.getTask();
 
         mAtm.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 87a5985..91b9449 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -53,14 +53,12 @@
 @RunWith(WindowTestRunner.class)
 public class AppChangeTransitionTests extends WindowTestsBase {
 
-    private Task mStack;
     private Task mTask;
     private ActivityRecord mActivity;
 
     public void setUpOnDisplay(DisplayContent dc) {
         mActivity = createActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
         mTask = mActivity.getTask();
-        mStack = mTask.getRootTask();
 
         // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
         RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -143,7 +141,7 @@
         // Reparenting to a display with different windowing mode may trigger
         // a change transition internally, but it should be cleaned-up once
         // the display change is complete.
-        mStack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true);
+        mTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index bc4f16e..8cad56a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -20,10 +20,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -74,11 +76,14 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         translucentOpening.setOccludesParent(false);
         translucentOpening.setVisible(false);
+        mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
         mDisplayContent.mOpeningApps.add(behind);
         mDisplayContent.mOpeningApps.add(translucentOpening);
+
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
-                mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
-                        TRANSIT_OLD_TASK_OPEN));
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                    mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                    null, null));
     }
 
     @Test
@@ -89,10 +94,12 @@
         final ActivityRecord translucentClosing = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         translucentClosing.setOccludesParent(false);
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
         mDisplayContent.mClosingApps.add(translucentClosing);
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
-                mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
-                        TRANSIT_OLD_TASK_CLOSE));
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null, null));
     }
 
     @Test
@@ -104,11 +111,13 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         translucentOpening.setOccludesParent(false);
         translucentOpening.setVisible(false);
+        mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE);
         mDisplayContent.mOpeningApps.add(behind);
         mDisplayContent.mOpeningApps.add(translucentOpening);
         assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
-                mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
-                        TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE));
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null, null));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 850e72e..6ca69bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -18,11 +18,12 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OPEN;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
@@ -71,40 +72,58 @@
 
     @Test
     public void testKeyguardOverride() {
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
+
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+        mDc.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */));
     }
 
     @Test
     public void testKeyguardKeep() {
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
-    }
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
 
-    @Test
-    public void testForceOverride() {
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
-        assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransitionOld());
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */));
     }
 
     @Test
     public void testCrashing() {
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransitionOld());
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
+
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+        mDc.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */));
     }
 
     @Test
     public void testKeepKeyguard_withCrashing() {
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
+
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+        mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+        mDc.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */));
     }
 
     @Test
@@ -123,12 +142,8 @@
 
         // Simulate activity resume / finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected for each display.
-        dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
-        assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
-        dc2.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
-        assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransitionOld());
+        dc1.prepareAppTransition(TRANSIT_OPEN);
+        dc2.prepareAppTransition(TRANSIT_CLOSE);
         // One activity window is visible for resuming & the other activity window is invisible
         // for finishing in different display.
         activity1.setVisibility(true, false);
@@ -156,9 +171,8 @@
         dc1.mClosingApps.add(activity1);
         assertTrue(dc1.mClosingApps.size() > 0);
 
-        dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
-        assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
+        dc1.prepareAppTransition(TRANSIT_OPEN);
+        assertTrue(dc1.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
         assertTrue(dc1.mAppTransition.isTransitionSet());
 
         dc1.mOpeningApps.add(activity1);
@@ -199,9 +213,8 @@
 
         // Simulate activity finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected.
-        dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
-        assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransitionOld());
+        dc.prepareAppTransition(TRANSIT_CLOSE);
+        assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_CLOSE));
         dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
         exitingActivity.setVisibility(false, false);
         assertTrue(dc.mClosingApps.size() > 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index f77454d..d899ebe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -65,7 +65,6 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -82,94 +81,87 @@
 @RunWith(WindowTestRunner.class)
 public class AppWindowTokenTests extends WindowTestsBase {
 
-    Task mStack;
-    Task mTask;
-    ActivityRecord mActivity;
-
     private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
 
-    @Before
-    public void setUp() throws Exception {
-        mStack = createTaskStackOnDisplay(mDisplayContent);
-        mTask = createTaskInStack(mStack, 0 /* userId */);
-        mActivity = createNonAttachedActivityRecord(mDisplayContent);
-
-        mTask.addChild(mActivity, 0);
-    }
-
     @Test
     @Presubmit
     public void testAddWindow_Order() {
-        assertEquals(0, mActivity.getChildCount());
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        assertEquals(0, activity.getChildCount());
 
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, mActivity, "win1");
-        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
+        final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1");
+        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
                 "startingWin");
-        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "baseWin");
-        final WindowState win4 = createWindow(null, TYPE_APPLICATION, mActivity, "win4");
+        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin");
+        final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4");
 
         // Should not contain the windows that were added above.
-        assertEquals(4, mActivity.getChildCount());
-        assertTrue(mActivity.mChildren.contains(win1));
-        assertTrue(mActivity.mChildren.contains(startingWin));
-        assertTrue(mActivity.mChildren.contains(baseWin));
-        assertTrue(mActivity.mChildren.contains(win4));
+        assertEquals(4, activity.getChildCount());
+        assertTrue(activity.mChildren.contains(win1));
+        assertTrue(activity.mChildren.contains(startingWin));
+        assertTrue(activity.mChildren.contains(baseWin));
+        assertTrue(activity.mChildren.contains(win4));
 
         // The starting window should be on-top of all other windows.
-        assertEquals(startingWin, mActivity.mChildren.peekLast());
+        assertEquals(startingWin, activity.mChildren.peekLast());
 
         // The base application window should be below all other windows.
-        assertEquals(baseWin, mActivity.mChildren.peekFirst());
-        mActivity.removeImmediately();
+        assertEquals(baseWin, activity.mChildren.peekFirst());
+        activity.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testFindMainWindow() {
-        assertNull(mActivity.findMainWindow());
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        assertNull(activity.findMainWindow());
 
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window12");
-        assertEquals(window1, mActivity.findMainWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11");
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12");
+        assertEquals(window1, activity.findMainWindow());
         window1.mAnimatingExit = true;
-        assertEquals(window1, mActivity.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
+        assertEquals(window1, activity.findMainWindow());
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity,
                 "window2");
-        assertEquals(window2, mActivity.findMainWindow());
-        mActivity.removeImmediately();
+        assertEquals(window2, activity.findMainWindow());
+        activity.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testGetTopFullscreenOpaqueWindow() {
-        assertNull(mActivity.getTopFullscreenOpaqueWindow());
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        assertNull(activity.getTopFullscreenOpaqueWindow());
 
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
-        final WindowState window11 = createWindow(null, TYPE_APPLICATION, mActivity, "window11");
-        final WindowState window12 = createWindow(null, TYPE_APPLICATION, mActivity, "window12");
-        assertEquals(window12, mActivity.getTopFullscreenOpaqueWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
+        final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11");
+        final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12");
+        assertEquals(window12, activity.getTopFullscreenOpaqueWindow());
         window12.mAttrs.width = 500;
-        assertEquals(window11, mActivity.getTopFullscreenOpaqueWindow());
+        assertEquals(window11, activity.getTopFullscreenOpaqueWindow());
         window11.mAttrs.width = 500;
-        assertEquals(window1, mActivity.getTopFullscreenOpaqueWindow());
+        assertEquals(window1, activity.getTopFullscreenOpaqueWindow());
         window1.mAttrs.alpha = 0f;
-        assertNull(mActivity.getTopFullscreenOpaqueWindow());
-        mActivity.removeImmediately();
+        assertNull(activity.getTopFullscreenOpaqueWindow());
+        activity.removeImmediately();
     }
 
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     @FlakyTest(bugId = 131005232)
     public void testLandscapeSeascapeRotationByApp() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(mDisplayContent).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopNonFinishingActivity();
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final TestWindowState appWindow = createWindowState(attrs, mActivity);
-        mActivity.addWindow(appWindow);
+        final TestWindowState appWindow = createWindowState(attrs, activity);
+        activity.addWindow(appWindow);
 
         // Set initial orientation and update.
-        mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         mDisplayContent.updateOrientation(
                 mDisplayContent.getRequestedOverrideConfiguration(),
                 null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -177,7 +169,7 @@
         appWindow.mResizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
-        mActivity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+        activity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
         mDisplayContent.updateOrientation(
                 mDisplayContent.getRequestedOverrideConfiguration(),
                 null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -192,14 +184,17 @@
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     public void testLandscapeSeascapeRotationByPolicy() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(mDisplayContent).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopNonFinishingActivity();
         // This instance has been spied in {@link TestDisplayContent}.
         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("RotationByPolicy");
-        final TestWindowState appWindow = createWindowState(attrs, mActivity);
-        mActivity.addWindow(appWindow);
+        final TestWindowState appWindow = createWindowState(attrs, activity);
+        activity.addWindow(appWindow);
 
         // Set initial orientation and update.
         performRotation(displayRotation, Surface.ROTATION_90);
@@ -220,48 +215,53 @@
     @Test
     @Presubmit
     public void testGetOrientation() {
-        mActivity.setVisible(true);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.setVisible(true);
 
-        mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        mActivity.setOccludesParent(false);
+        activity.setOccludesParent(false);
         // Can specify orientation if app doesn't occludes parent.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation());
 
-        mActivity.setOccludesParent(true);
-        mActivity.setVisible(false);
+        activity.setOccludesParent(true);
+        activity.setVisible(false);
         // Can not specify orientation if app isn't visible even though it occludes parent.
-        assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
-                mActivity.getOrientation(SCREEN_ORIENTATION_BEHIND));
+                activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
     }
 
     @Test
     @Presubmit
     public void testKeyguardFlagsDuringRelaunch() {
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
         attrs.setTitle("AppWindow");
-        final TestWindowState appWindow = createWindowState(attrs, mActivity);
+        final TestWindowState appWindow = createWindowState(attrs, activity);
 
         // Add window with show when locked flag
-        mActivity.addWindow(appWindow);
-        assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+        activity.addWindow(appWindow);
+        assertTrue(activity.containsShowWhenLockedWindow()
+                && activity.containsDismissKeyguardWindow());
 
         // Start relaunching
-        mActivity.startRelaunching();
-        assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+        activity.startRelaunching();
+        assertTrue(activity.containsShowWhenLockedWindow()
+                && activity.containsDismissKeyguardWindow());
 
         // Remove window and make sure that we still report back flag
-        mActivity.removeChild(appWindow);
-        assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+        activity.removeChild(appWindow);
+        assertTrue(activity.containsShowWhenLockedWindow()
+                && activity.containsDismissKeyguardWindow());
 
         // Finish relaunching and ensure flag is now not reported
-        mActivity.finishRelaunching();
-        assertFalse(
-                mActivity.containsShowWhenLockedWindow() || mActivity.containsDismissKeyguardWindow());
+        activity.finishRelaunching();
+        assertFalse(activity.containsShowWhenLockedWindow()
+                || activity.containsDismissKeyguardWindow());
     }
 
     @Test
@@ -281,17 +281,18 @@
 
     @Test
     public void testSetOrientation() {
-        mActivity.setVisible(true);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.setVisible(true);
 
         // Assert orientation is unspecified to start.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.getOrientation());
 
-        mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
+        activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation());
 
-        mDisplayContent.removeAppToken(mActivity.token);
+        mDisplayContent.removeAppToken(activity.token);
         // Assert orientation is unset to after container is removed.
-        assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
 
         // Reset display frozen state
         mWm.mDisplayFrozen = false;
@@ -300,14 +301,15 @@
     @UseTestDisplay
     @Test
     public void testRespectTopFullscreenOrientation() {
-        final Configuration displayConfig = mActivity.mDisplayContent.getConfiguration();
-        final Configuration activityConfig = mActivity.getConfiguration();
-        mActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Configuration displayConfig = activity.mDisplayContent.getConfiguration();
+        final Configuration activityConfig = activity.getConfiguration();
+        activity.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation);
 
-        final ActivityRecord topActivity = createActivityRecord(mTask);
+        final ActivityRecord topActivity = createActivityRecord(activity.getTask());
         topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, displayConfig.orientation);
@@ -322,32 +324,36 @@
     @UseTestDisplay
     @Test
     public void testReportOrientationChange() {
-        mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(mDisplayContent).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopNonFinishingActivity();
+        activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         mDisplayContent.getDisplayRotation().setFixedToUserRotation(
                 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
-        reset(mTask);
-        mActivity.reportDescendantOrientationChangeIfNeeded();
-        verify(mTask).onConfigurationChanged(any(Configuration.class));
+        reset(task);
+        activity.reportDescendantOrientationChangeIfNeeded();
+        verify(task).onConfigurationChanged(any(Configuration.class));
     }
 
     @Test
     @FlakyTest(bugId = 131176283)
     public void testCreateRemoveStartingWindow() {
-        mActivity.addStartingWindow(mPackageName,
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false);
         waitUntilHandlersIdle();
-        assertHasStartingWindow(mActivity);
-        mActivity.removeStartingWindow();
+        assertHasStartingWindow(activity);
+        activity.removeStartingWindow();
         waitUntilHandlersIdle();
-        assertNoStartingWindow(mActivity);
+        assertNoStartingWindow(activity);
     }
 
     @Test
     public void testAddRemoveRace() {
         // There was once a race condition between adding and removing starting windows
-        final ActivityRecord appToken = createIsolatedTestActivityRecord();
+        final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
         for (int i = 0; i < 1000; i++) {
             appToken.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -360,8 +366,8 @@
 
     @Test
     public void testTransferStartingWindow() {
-        final ActivityRecord activity1 = createIsolatedTestActivityRecord();
-        final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false);
@@ -376,8 +382,8 @@
 
     @Test
     public void testTransferStartingWindowWhileCreating() {
-        final ActivityRecord activity1 = createIsolatedTestActivityRecord();
-        final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
                 () -> {
                     // Surprise, ...! Transfer window in the middle of the creation flow.
@@ -396,8 +402,8 @@
 
     @Test
     public void testTransferStartingWindowCanAnimate() {
-        final ActivityRecord activity1 = createIsolatedTestActivityRecord();
-        final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false);
@@ -419,34 +425,34 @@
 
     @Test
     public void testTransferStartingWindowFromFinishingActivity() {
-        mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = activity.getTask();
+        activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
                 "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */,
                 null /* transferFrom */, true /* newTask */, true /* taskSwitch */,
                 false /* processRunning */, false /* allowTaskSnapshot */,
                 false /* activityCreate */);
         waitUntilHandlersIdle();
-        assertHasStartingWindow(mActivity);
-        mActivity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
+        assertHasStartingWindow(activity);
+        activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
 
-        doCallRealMethod().when(mStack).startActivityLocked(
+        doCallRealMethod().when(task).startActivityLocked(
                 any(), any(), anyBoolean(), anyBoolean(), any());
         // Make mVisibleSetFromTransferredStartingWindow true.
-        final ActivityRecord middle = new ActivityBuilder(mWm.mAtmService)
-                .setTask(mTask).build();
-        mStack.startActivityLocked(middle, null /* focusedTopActivity */,
+        final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
+        task.startActivityLocked(middle, null /* focusedTopActivity */,
                 false /* newTask */, false /* keepCurTransition */, null /* options */);
         middle.makeFinishingLocked();
 
-        assertNull(mActivity.mStartingWindow);
+        assertNull(activity.mStartingWindow);
         assertHasStartingWindow(middle);
 
-        final ActivityRecord top = new ActivityBuilder(mWm.mAtmService)
-                .setTask(mTask).build();
+        final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build();
         // Expect the visibility should be updated to true when transferring starting window from
         // a visible activity.
         top.setVisible(false);
         // The finishing middle should be able to transfer starting window to top.
-        mStack.startActivityLocked(top, null /* focusedTopActivity */,
+        task.startActivityLocked(top, null /* focusedTopActivity */,
                 false /* newTask */, false /* keepCurTransition */, null /* options */);
 
         assertNull(middle.mStartingWindow);
@@ -459,10 +465,12 @@
 
     @Test
     public void testTransferStartingWindowSetFixedRotation() {
-        final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = activity.getTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.setVisible(false);
-        mTask.positionChildAt(topActivity, POSITION_TOP);
-        mActivity.addStartingWindow(mPackageName,
+        task.positionChildAt(topActivity, POSITION_TOP);
+        activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false);
         waitUntilHandlersIdle();
@@ -470,38 +478,25 @@
         // Make activities to have different rotation from it display and set fixed rotation
         // transform to activity1.
         int rotation = (mDisplayContent.getRotation() + 1) % 4;
-        mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation);
+        mDisplayContent.setFixedRotationLaunchingApp(activity, rotation);
         doReturn(rotation).when(mDisplayContent)
                 .rotationForActivityInDifferentOrientation(topActivity);
 
         // Make sure the fixed rotation transform linked to activity2 when adding starting window
         // on activity2.
         topActivity.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
                 false, false, false, true, false);
         waitUntilHandlersIdle();
         assertTrue(topActivity.hasFixedRotationTransform());
     }
 
-    private ActivityRecord createIsolatedTestActivityRecord() {
-        final Task taskStack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(taskStack, 0 /* userId */);
-        return createTestActivityRecordForGivenTask(task);
-    }
-
-    private ActivityRecord createTestActivityRecordForGivenTask(Task task) {
-        final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
-        task.addChild(activity, 0);
-        waitUntilHandlersIdle();
-        return activity;
-    }
-
     @Test
     public void testTryTransferStartingWindowFromHiddenAboveToken() {
         // Add two tasks on top of each other.
-        final ActivityRecord activityTop = createIsolatedTestActivityRecord();
-        final ActivityRecord activityBottom =
-                createTestActivityRecordForGivenTask(activityTop.getTask());
+        final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build();
+        activityTop.getTask().addChild(activityBottom, 0);
 
         // Add a starting window.
         activityTop.addStartingWindow(mPackageName,
@@ -523,53 +518,58 @@
     @Test
     public void testTransitionAnimationBounds() {
         removeGlobalMinSizeRestriction();
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateParentTask(true).setCreateActivity(true).build();
+        final Task rootTask = task.getRootTask();
+        final ActivityRecord activity = task.getTopNonFinishingActivity();
         final Rect stackBounds = new Rect(0, 0, 1000, 600);
         final Rect taskBounds = new Rect(100, 400, 600, 800);
         // Set the bounds and windowing mode to window configuration directly, otherwise the
         // testing setups may be discarded by configuration resolving.
-        mStack.getWindowConfiguration().setBounds(stackBounds);
-        mTask.getWindowConfiguration().setBounds(taskBounds);
-        mActivity.getWindowConfiguration().setBounds(taskBounds);
+        rootTask.getWindowConfiguration().setBounds(stackBounds);
+        task.getWindowConfiguration().setBounds(taskBounds);
+        activity.getWindowConfiguration().setBounds(taskBounds);
 
         // Check that anim bounds for freeform window match task bounds
-        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE));
+        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_NONE));
 
         // STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
         // bounds animation layer.
-        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
 
         // Even the activity is smaller than task and it is not aligned to the top-left corner of
         // task, the animation bounds the same as task and position should be zero because in real
         // case the letterbox will fill the remaining area in task.
         final Rect halfBounds = new Rect(taskBounds);
         halfBounds.scale(0.5f);
-        mActivity.getWindowConfiguration().setBounds(halfBounds);
+        activity.getWindowConfiguration().setBounds(halfBounds);
         final Point animationPosition = new Point();
-        mActivity.getAnimationPosition(animationPosition);
+        activity.getAnimationPosition(animationPosition);
 
-        assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+        assertEquals(taskBounds, activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
         assertEquals(new Point(0, 0), animationPosition);
 
         // STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
-        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
+        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        assertEquals(rootTask.getBounds(), activity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
     }
 
     @Test
     public void testHasStartingWindow() {
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final WindowManager.LayoutParams attrs =
                 new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
-        final TestWindowState startingWindow = createWindowState(attrs, mActivity);
-        mActivity.startingDisplayed = true;
-        mActivity.addWindow(startingWindow);
-        assertTrue("Starting window should be present", mActivity.hasStartingWindow());
-        mActivity.startingDisplayed = false;
-        assertTrue("Starting window should be present", mActivity.hasStartingWindow());
+        final TestWindowState startingWindow = createWindowState(attrs, activity);
+        activity.startingDisplayed = true;
+        activity.addWindow(startingWindow);
+        assertTrue("Starting window should be present", activity.hasStartingWindow());
+        activity.startingDisplayed = false;
+        assertTrue("Starting window should be present", activity.hasStartingWindow());
 
-        mActivity.removeChild(startingWindow);
-        assertFalse("Starting window should not be present", mActivity.hasStartingWindow());
+        activity.removeChild(startingWindow);
+        assertFalse("Starting window should not be present", activity.hasStartingWindow());
     }
 
     private void assertHasStartingWindow(ActivityRecord atoken) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index cfe956f..06a6882 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -52,8 +50,6 @@
 
     private DisplayAreaGroup mDisplayAreaGroup;
     private TaskDisplayArea mTaskDisplayArea;
-    private Task mStack;
-    private ActivityRecord mActivity;
 
     @Before
     public void setUp() {
@@ -65,9 +61,6 @@
         mTaskDisplayArea = new TaskDisplayArea(
                 mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1);
         mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP);
-        mStack = mTaskDisplayArea.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mActivity = new ActivityBuilder(mAtm).setTask(mStack).build();
         mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea);
     }
 
@@ -91,28 +84,31 @@
 
     @Test
     public void testGetRequestedOrientationForDisplay() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopNonFinishingActivity();
         doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any());
-        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         // Display is portrait, DisplayAreaGroup inherits that
         mDisplayContent.setBounds(0, 0, 600, 900);
 
         assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
-        assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+        assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
                 .isEqualTo(ORIENTATION_PORTRAIT);
 
         // DisplayAreaGroup is landscape, different from Display
         mDisplayAreaGroup.setBounds(0, 0, 600, 450);
 
         assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
-        assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+        assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
                 .isEqualTo(ORIENTATION_LANDSCAPE);
 
         // DisplayAreaGroup is portrait, same as Display
         mDisplayAreaGroup.setBounds(0, 0, 300, 900);
 
         assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
-        assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+        assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
                 .isEqualTo(ORIENTATION_PORTRAIT);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0f7a254..d921718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1186,8 +1186,7 @@
 
         final ActivityRecord app = mAppWindow.mActivityRecord;
         app.setVisible(false);
-        mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */);
+        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
         mDisplayContent.mOpeningApps.add(app);
         final int newOrientation = getRotatedOrientation(mDisplayContent);
         app.setRequestedOrientation(newOrientation);
@@ -1370,8 +1369,7 @@
         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
         app.setVisible(false);
         app.setState(Task.ActivityState.RESUMED, "test");
-        mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */);
+        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
         mDisplayContent.mOpeningApps.add(app);
         final int newOrientation = getRotatedOrientation(mDisplayContent);
         app.setRequestedOrientation(newOrientation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5641fe2..70d47a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -46,7 +46,6 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
 import android.view.DragEvent;
 import android.view.IWindowSessionCallback;
@@ -59,6 +58,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 
 import org.junit.After;
 import org.junit.AfterClass;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index cc92ddd..40f73b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -252,7 +252,7 @@
         assertNull(mController.mRecentScreenshotAnimator);
 
         // Simulate the app transition finishing
-        mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
+        mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0);
         verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 62aa02f..42193c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -17,11 +17,6 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -35,7 +30,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -58,57 +52,6 @@
 @RunWith(WindowTestRunner.class)
 public class RootWindowContainerTests extends WindowTestsBase {
 
-    private static final int FAKE_CALLING_UID = 667;
-
-    @Test
-    public void testIsAnyNonToastWindowVisibleForUid_oneToastOneAppStartOneNonToastBothVisible() {
-        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
-        final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting",
-                FAKE_CALLING_UID);
-        toastyToast.mHasSurface = true;
-        app.mHasSurface = true;
-        appStart.mHasSurface = true;
-
-        assertTrue(toastyToast.isVisibleNow());
-        assertTrue(app.isVisibleNow());
-        assertTrue(appStart.isVisibleNow());
-        assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
-    }
-
-    @Test
-    public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() {
-        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
-        toastyToast.mHasSurface = true;
-
-        assertTrue(toastyToast.isVisibleNow());
-        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
-    }
-
-    @Test
-    public void testIsAnyNonToastWindowVisibleForUid_onlyAppStartingVisible() {
-        final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting",
-                FAKE_CALLING_UID);
-        appStart.mHasSurface = true;
-
-        assertTrue(appStart.isVisibleNow());
-        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
-    }
-
-    @Test
-    public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
-        final WindowState statusBar =
-                createWindow(null, TYPE_STATUS_BAR, "statusBar", FAKE_CALLING_UID);
-        final WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE,
-                "notificationShade", FAKE_CALLING_UID);
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
-
-        assertFalse(statusBar.isVisibleNow());
-        assertFalse(notificationShade.isVisibleNow());
-        assertFalse(app.isVisibleNow());
-        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
-    }
-
     @Test
     public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() {
         assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ddc29f5..a4bf594 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -72,13 +72,11 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class SizeCompatTests extends WindowTestsBase {
-    private Task mStack;
     private Task mTask;
     private ActivityRecord mActivity;
 
     private void setUpApp(DisplayContent display) {
-        mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
-        mTask = mStack.getBottomMostTask();
+        mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
         mActivity = mTask.getTopNonFinishingActivity();
     }
 
@@ -97,7 +95,7 @@
         prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
 
         final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
-        resizeDisplay(mStack.mDisplayContent, 600, 1200);
+        resizeDisplay(mTask.mDisplayContent, 600, 1200);
         // The visible activity should recompute configuration according to the last parent bounds.
         mAtm.restartActivityProcessIfVisible(mActivity.appToken);
 
@@ -196,7 +194,7 @@
         final int originalDpi = mActivity.getConfiguration().densityDpi;
 
         // Move the non-resizable activity to the new display.
-        mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+        mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
 
         assertEquals(originalBounds.width(), mActivity.getBounds().width());
         assertEquals(originalBounds.height(), mActivity.getBounds().height());
@@ -321,7 +319,7 @@
                 .setCanRotate(false).setNotch(notchHeight).build();
 
         // Move the non-resizable activity to the new display.
-        mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+        mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
         // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
         assertEquals(origWidth, configBounds.width());
         assertEquals(origHeight, configBounds.height());
@@ -363,7 +361,7 @@
 
         // Although the activity is fixed orientation, force rotate the display.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
-        assertEquals(ROTATION_270, mStack.getWindowConfiguration().getRotation());
+        assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
 
         assertEquals(origBounds.width(), currentBounds.width());
         // The notch is on horizontal side, so current height changes from 1460 to 1400.
@@ -436,7 +434,7 @@
     public void testResetNonVisibleActivity() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
-        final DisplayContent display = mStack.mDisplayContent;
+        final DisplayContent display = mTask.mDisplayContent;
         // Resize the display so the activity is in size compatibility mode.
         resizeDisplay(display, 900, 1800);
 
@@ -488,7 +486,7 @@
                 });
 
         // Resize the display so that the activity exercises size-compat mode.
-        resizeDisplay(mStack.mDisplayContent, 1000, 2500);
+        resizeDisplay(mTask.mDisplayContent, 1000, 2500);
 
         // Expect the exact token when the activity is in size compatibility mode.
         assertEquals(1, compatTokens.size());
@@ -501,7 +499,7 @@
         activity.restartProcessIfVisible();
         // The full lifecycle isn't hooked up so manually set state to resumed
         activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
-        mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
+        mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
 
         // Expect null token when switching to non-size-compat mode activity.
         assertEquals(1, compatTokens.size());
@@ -525,13 +523,13 @@
 
         // The non-resizable activity should not be size compat because it is on a resizable task
         // in multi-window mode.
-        mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         assertFalse(activity.shouldUseSizeCompatMode());
 
         // The non-resizable activity should not be size compat because the display support
         // changing windowing mode from fullscreen to freeform.
-        mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+        mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         assertFalse(activity.shouldUseSizeCompatMode());
     }
 
@@ -544,8 +542,7 @@
         addStatusBar(mActivity.mDisplayContent);
 
         mActivity.setVisible(false);
-        mActivity.mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
-                false /* alwaysKeepCurrent */);
+        mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
         mActivity.mDisplayContent.mOpeningApps.add(mActivity);
         final float maxAspect = 1.8f;
         prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
@@ -784,7 +781,7 @@
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
-        mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+        mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
 
         // App still in size compat, and the bounds don't change.
         verify(mActivity, never()).clearSizeCompatMode();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 4bd8edd..28ba710 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -70,23 +70,22 @@
 @RunWith(WindowTestRunner.class)
 public class TaskDisplayAreaTests extends WindowTestsBase {
 
-    private Task mPinnedStack;
+    private Task mPinnedTask;
 
     @Before
     public void setUp() throws Exception {
-        mPinnedStack = createTaskStackOnDisplay(
+        mPinnedTask = createTaskStackOnDisplay(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         // Stack should contain visible app window to be considered visible.
-        final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
-        assertFalse(mPinnedStack.isVisible());
+        assertFalse(mPinnedTask.isVisible());
         final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
-        pinnedTask.addChild(pinnedApp, 0 /* addPos */);
-        assertTrue(mPinnedStack.isVisible());
+        mPinnedTask.addChild(pinnedApp, 0 /* addPos */);
+        assertTrue(mPinnedTask.isVisible());
     }
 
     @After
     public void tearDown() throws Exception {
-        mPinnedStack.removeImmediately();
+        mPinnedTask.removeImmediately();
     }
 
     @Test
@@ -118,19 +117,19 @@
 
         final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
         final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
+        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
         assertThat(pinnedStackPos).isGreaterThan(stack2Pos);
         assertThat(stack2Pos).isGreaterThan(stack1Pos);
 
-        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false);
+        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false);
         assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
         assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
 
-        taskStackContainer.positionChildAt(1, mPinnedStack, false);
+        taskStackContainer.positionChildAt(1, mPinnedTask, false);
         assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
         assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
     }
 
     @Test
@@ -141,16 +140,16 @@
         final WindowContainer taskStackContainer = stack1.getParent();
 
         final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
+        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
         assertThat(pinnedStackPos).isGreaterThan(stackPos);
 
         taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
         assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
 
         taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
         assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 0b99e32..0bb2867 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 
@@ -41,7 +42,6 @@
 import android.hardware.HardwareBuffer;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -135,7 +135,7 @@
         final long id = 1234L;
         final ComponentName activityComponent = new ComponentName("package", ".Class");
         final int windowingMode = WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-        final int systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN;
+        final int appearance = APPEARANCE_LIGHT_STATUS_BARS;
         final int pixelFormat = PixelFormat.RGBA_8888;
         final int orientation = Configuration.ORIENTATION_PORTRAIT;
         final float scaleFraction = 0.25f;
@@ -147,7 +147,7 @@
                     new ActivityManager.TaskSnapshot.Builder();
             builder.setId(id);
             builder.setTopActivityComponent(activityComponent);
-            builder.setSystemUiVisibility(systemUiVisibility);
+            builder.setAppearance(appearance);
             builder.setWindowingMode(windowingMode);
             builder.setColorSpace(sRGB);
             builder.setOrientation(orientation);
@@ -164,7 +164,7 @@
             ActivityManager.TaskSnapshot snapshot = builder.build();
             assertEquals(id, snapshot.getId());
             assertEquals(activityComponent, snapshot.getTopActivityComponent());
-            assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
+            assertEquals(appearance, snapshot.getAppearance());
             assertEquals(windowingMode, snapshot.getWindowingMode());
             assertEquals(sRGB, snapshot.getColorSpace());
             // Snapshots created with the Builder class are always high-res. The only way to get a
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index bfee894..8b90824 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 
@@ -36,7 +38,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
 import android.view.Surface;
-import android.view.View;
 
 import androidx.test.filters.MediumTest;
 
@@ -362,17 +363,16 @@
     }
 
     @Test
-    public void testSystemUiVisibilityPersistAndLoadSnapshot() {
-        final int lightBarFlags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
-                | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+    public void testAppearancePersistAndLoadSnapshot() {
+        final int lightBarFlags = APPEARANCE_LIGHT_STATUS_BARS | APPEARANCE_LIGHT_NAVIGATION_BARS;
         TaskSnapshot a = new TaskSnapshotBuilder()
                 .setSystemUiVisibility(0)
                 .build();
         TaskSnapshot b = new TaskSnapshotBuilder()
                 .setSystemUiVisibility(lightBarFlags)
                 .build();
-        assertEquals(0, a.getSystemUiVisibility());
-        assertEquals(lightBarFlags, b.getSystemUiVisibility());
+        assertEquals(0, a.getAppearance());
+        assertEquals(lightBarFlags, b.getAppearance());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -382,8 +382,8 @@
                 false /* isLowResolution */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertEquals(0, snapshotA.getSystemUiVisibility());
-        assertEquals(lightBarFlags, snapshotB.getSystemUiVisibility());
+        assertEquals(0, snapshotA.getAppearance());
+        assertEquals(lightBarFlags, snapshotB.getAppearance());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 0b0341a..fcd46a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -41,10 +41,10 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.view.Surface;
 
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 
 import org.junit.After;
 import org.junit.AfterClass;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index e0c72fb..3d8adbd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -23,6 +23,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -919,7 +920,7 @@
                     }
                 }, 0, 0, false);
         adapter.setCallingPidUid(123, 456);
-        wc.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_TASK_OPEN, false);
+        wc.getDisplayContent().prepareAppTransition(TRANSIT_OPEN);
         wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
         spyOn(wc);
         doReturn(true).when(wc).okToAnimate();
@@ -943,8 +944,7 @@
 
         // Make sure animation finish callback will be received and reset animating state after
         // animation finish.
-        wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act,
-                mDisplayContent.mOpeningApps);
+        wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act);
         verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any());
         assertFalse(wc.isAnimating());
         assertFalse(act.isAnimating(PARENTS));
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 75d2c51..1d498bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -35,6 +35,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -731,6 +732,39 @@
                 handle.inputFeatures);
     }
 
+    @Test
+    public void testHasActiveVisibleWindow() {
+        final int uid = ActivityBuilder.DEFAULT_FAKE_UID;
+        mAtm.mActiveUids.onUidActive(uid, 0 /* any proc state */);
+
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid);
+        app.mActivityRecord.setVisible(false);
+        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        assertFalse(mAtm.hasActiveVisibleWindow(uid));
+
+        app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+        assertTrue(mAtm.hasActiveVisibleWindow(uid));
+
+        // Make the activity invisible and add a visible toast. The uid should have no active
+        // visible window because toast can be misused by legacy app to bypass background check.
+        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid);
+        final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid);
+        toast.onSurfaceShownChanged(true);
+        assertFalse(mAtm.hasActiveVisibleWindow(uid));
+
+        // Though starting window should belong to system. Make sure it is ignored to avoid being
+        // allow-list unexpectedly, see b/129563343.
+        final WindowState starting =
+                createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid);
+        starting.onSurfaceShownChanged(true);
+        assertFalse(mAtm.hasActiveVisibleWindow(uid));
+
+        // Make the application overlay window visible. It should be a valid active visible window.
+        overlay.onSurfaceShownChanged(true);
+        assertTrue(mAtm.hasActiveVisibleWindow(uid));
+    }
+
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     public void testNeedsRelativeLayeringToIme_notAttached() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ee1034c..8305381 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -637,6 +637,7 @@
      * Builder for creating new activities.
      */
     protected static class ActivityBuilder {
+        static final int DEFAULT_FAKE_UID = 12345;
         // An id appended to the end of the component name to make it unique
         private static int sCurrentActivityId = 0;
 
@@ -647,7 +648,7 @@
         private Task mTask;
         private String mProcessName = "name";
         private String mAffinity;
-        private int mUid = 12345;
+        private int mUid = DEFAULT_FAKE_UID;
         private boolean mCreateTask = false;
         private Task mParentTask;
         private int mActivityFlags;
@@ -840,10 +841,13 @@
                 // to set it somewhere else since we can't mock resources.
                 doReturn(true).when(activity).occludesParent();
                 doReturn(true).when(activity).fillsParent();
+                mTask.addChild(activity);
                 if (mOnTop) {
+                    // Move the task to front after activity added.
+                    // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks
+                    // (e.g. home stack).
                     mTask.moveToFront("createActivity");
                 }
-                mTask.addChild(activity);
                 // Make visible by default...
                 activity.setVisible(true);
             }
@@ -854,7 +858,7 @@
             } else {
                 wpc = new WindowProcessController(mService,
                         aInfo.applicationInfo, mProcessName, mUid,
-                        UserHandle.getUserId(12345), mock(Object.class),
+                        UserHandle.getUserId(mUid), mock(Object.class),
                         mock(WindowProcessListener.class));
                 wpc.setThread(mock(IApplicationThread.class));
             }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 632ad4c..4bf93a2 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -60,7 +60,6 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.UUID;
-import java.util.function.BiFunction;
 
 /**
  * Helper for {@link SoundTrigger} APIs. Supports two types of models:
@@ -118,8 +117,7 @@
 
     private PowerSaveModeListener mPowerSaveModeListener;
 
-    private final BiFunction<Integer, SoundTrigger.StatusListener, SoundTriggerModule>
-            mModuleProvider;
+    private final SoundTriggerModuleProvider mModuleProvider;
 
     // Handler to process call state changes will delay to allow time for the audio
     // and sound trigger HALs to process the end of call notifications
@@ -128,12 +126,32 @@
     private static final int MSG_CALL_STATE_CHANGED = 0;
     private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
 
-    SoundTriggerHelper(Context context,
-            @NonNull BiFunction<Integer, SoundTrigger.StatusListener,
-                    SoundTriggerModule> moduleProvider) {
+    /**
+     * Provider interface for retrieving SoundTriggerModule instances
+     */
+    public interface SoundTriggerModuleProvider {
+        /**
+         * Populate module properties for all available modules
+         *
+         * @param modules List of ModuleProperties to be populated
+         * @return Status int 0 on success.
+         */
+        int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules);
+
+        /**
+         * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()}
+         *
+         * @param moduleId Module ID
+         * @param statusListener Client listener to be associated with the returned module
+         * @return Module associated with moduleId
+         */
+        SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener);
+    }
+
+    SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) {
         ArrayList <ModuleProperties> modules = new ArrayList<>();
         mModuleProvider = moduleProvider;
-        int status = SoundTrigger.listModules(modules);
+        int status = mModuleProvider.listModuleProperties(modules);
         mContext = context;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -272,7 +290,7 @@
 
     private int prepareForRecognition(ModelData modelData) {
         if (mModule == null) {
-            mModule = mModuleProvider.apply(mModuleProperties.getId(), this);
+            mModule = mModuleProvider.getModule(mModuleProperties.getId(), this);
             if (mModule == null) {
                 Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
                 return STATUS_ERROR;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 2a5bfce..bd678fd 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -51,6 +51,7 @@
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.hardware.soundtrigger.SoundTriggerModule;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioRecord;
@@ -219,9 +220,20 @@
         Identity originatorIdentity = IdentityContext.getNonNull();
 
         return new SoundTriggerHelper(mContext,
-                (moduleId, listener) -> SoundTrigger.attachModuleAsMiddleman(moduleId, listener,
-                        null,
-                        middlemanIdentity, originatorIdentity));
+                new SoundTriggerHelper.SoundTriggerModuleProvider() {
+                    @Override
+                    public int listModuleProperties(ArrayList<ModuleProperties> modules) {
+                        return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity,
+                                originatorIdentity);
+                    }
+
+                    @Override
+                    public SoundTriggerModule getModule(int moduleId,
+                            SoundTrigger.StatusListener statusListener) {
+                        return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null,
+                                middlemanIdentity, originatorIdentity);
+                    }
+                });
     }
 
     class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 657a7dd..06c23de 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -23,7 +23,6 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
@@ -49,7 +48,6 @@
 import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.Identity;
 import android.media.permission.IdentityContext;
 import android.media.permission.PermissionUtil;
@@ -66,7 +64,6 @@
 import android.os.ShellCallback;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
@@ -94,6 +91,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.soundtrigger.SoundTriggerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 031c337..f900c38 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -634,4 +634,15 @@
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
     public @interface OverrideNetworkType {}
+
+    /**
+     *  Result of a thermal mitigation request.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "THERMAL_MITIGATION_RESULT_" }, value = {
+        TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR})
+    public @interface ThermalMitigationResult {}
 }
diff --git a/telephony/java/android/telephony/DataThrottlingRequest.aidl b/telephony/java/android/telephony/DataThrottlingRequest.aidl
new file mode 100644
index 0000000..e1a3b66
--- /dev/null
+++ b/telephony/java/android/telephony/DataThrottlingRequest.aidl
@@ -0,0 +1,19 @@
+/*
+* 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;
+
+parcelable DataThrottlingRequest;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/DataThrottlingRequest.java b/telephony/java/android/telephony/DataThrottlingRequest.java
new file mode 100644
index 0000000..f50bb58
--- /dev/null
+++ b/telephony/java/android/telephony/DataThrottlingRequest.java
@@ -0,0 +1,244 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Class stores information related to the type of data throttling request. Must be populated as
+ * field in {@link ThermalMitigationRequest} for sending of thermal mitigation request at {@link
+ * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}.
+ * @hide
+ */
+@SystemApi
+public final class DataThrottlingRequest implements Parcelable {
+    /**
+     * Clear all existing data throttling, enable data, and attempt to enable radio for thermal
+     * mitigation all within the requested completion window. Note that attempting to enable radio
+     * will not guarantee that radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0;
+
+    /**
+     * Enact secondary carrier data throttling within specified completion window. This also
+     * attempts to enables radio if currently disabled for thermal mitigation, enables data, and
+     * removes any existing data throttling on primary carrier. Note that attempting to enable radio
+     * will not guarantee that radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1;
+
+    /**
+     * Enact primary carrier data throttling within specified completion window. This also attempts
+     * to enable radio if currently disabled for thermal mitigation and disables data on secondary
+     * carrier if currently enabled. Note that attempting to enable radio will not guarantee that
+     * radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2;
+
+    /**
+     * Immediately hold on to the current level of data throttling indicating that the current level
+     * of data throttling has alleviated the thermal concerns which caused the original data
+     * throttling request. A thermal module should remain actively monitoring the temperature levels
+     * and request an appropriate thermal mitigation action. {@link
+     * #THERMAL_MITIGATION_RESULT_INVALID_PARAMETERS} will be returned if completion window is not
+     * 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DATA_THROTTLING_ACTION_HOLD = 3;
+
+    /**
+     * Type of data throttling action to carry out.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DATA_THROTTLING_ACTION_" }, value = {
+        DATA_THROTTLING_ACTION_NO_DATA_THROTTLING,
+        DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER,
+        DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER,
+        DATA_THROTTLING_ACTION_HOLD})
+    public @interface DataThrottlingAction {}
+
+    /**
+     * Represents the data throttling action that will be requested. See {@link
+     * DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}, {@link
+     * #DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, {@link
+     * #DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}, and {@link
+     * #DATA_THROTTLING_ACTION_HOLD} for more details.
+     **/
+    private @DataThrottlingAction int mDataThrottlingAction;
+    /**
+     * Represents the time over which modem should gradually execute the data thorttling request.
+     */
+    private long mCompletionDurationMillis;
+
+    private DataThrottlingRequest(@NonNull int dataThrottlingAction,
+            long completionDurationMillis) {
+        mDataThrottlingAction = dataThrottlingAction;
+        mCompletionDurationMillis = completionDurationMillis;
+    }
+
+    private DataThrottlingRequest(Parcel in) {
+        mDataThrottlingAction = in.readInt();
+        mCompletionDurationMillis = in.readLong();
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDataThrottlingAction);
+        dest.writeLong(mCompletionDurationMillis);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "[DataThrottlingRequest "
+            + ", DataThrottlingAction=" + mDataThrottlingAction
+            + ", completionDurationMillis=" + mCompletionDurationMillis
+            + "]";
+    }
+
+    /**
+    * @return the dataThrottlingAction.
+    */
+    public @DataThrottlingAction int getDataThrottlingAction() {
+        return mDataThrottlingAction;
+    }
+
+    /**
+     * @return the completionDurationMillis which represents the time over which modem should
+     * gradually execute the data thorttling request.
+     */
+    public long getCompletionDurationMillis() {
+        return mCompletionDurationMillis;
+    }
+
+    public static final @NonNull Parcelable.Creator<DataThrottlingRequest> CREATOR =
+            new Parcelable.Creator<DataThrottlingRequest>() {
+
+        @Override
+        public DataThrottlingRequest createFromParcel(Parcel in) {
+            return new DataThrottlingRequest(in);
+        }
+
+        @Override
+        public DataThrottlingRequest[] newArray(int size) {
+            return new DataThrottlingRequest[size];
+        }
+    };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link DataThrottlingRequest} when creating
+     * a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code DataThrottlingRequest}:
+     *
+     * <pre><code>
+     *
+     * DataThrottlingRequest dp = new DataThrottlingRequest.Builder()
+     *     .setDataThrottlingAction(
+     *          DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER)
+     *     .setCompletionDurationMillis(10000L)
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private @DataThrottlingAction int mDataThrottlingAction;
+        private long mCompletionDurationMillis;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Set the data throttling action.
+         *
+         * @param dataThrottlingAction data throttling action.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDataThrottlingAction(
+                @DataThrottlingAction int dataThrottlingAction) {
+            mDataThrottlingAction = dataThrottlingAction;
+            return this;
+        }
+
+        /**
+         * Set the completion duration.
+         *
+         * @param completionDurationMillis completion duration in millis which represents the time
+         *      over which modem should gradually execute the data thorttling request. This can
+         *      never be a negative number and must be 0 for {@link #DATA_THROTTLING_ACTION_HOLD}.
+         *      Otherwise, an IllegalArgumentException will be thrown.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCompletionDurationMillis(long completionDurationMillis) {
+            mCompletionDurationMillis = completionDurationMillis;
+            return this;
+        }
+
+        /**
+         * Build the DataThrottlingRequest.
+         *
+         * @return the DataThrottlingRequest object.
+         */
+        public @NonNull DataThrottlingRequest build() {
+            if (mCompletionDurationMillis < 0) {
+                throw new IllegalArgumentException("completionDurationMillis cannot be a negative "
+                        + "number");
+            }
+
+            if (mDataThrottlingAction == DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD
+                    && mCompletionDurationMillis != 0) {
+                throw new IllegalArgumentException("completionDurationMillis must be 0 for "
+                    + "DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD");
+            }
+
+            return new DataThrottlingRequest(mDataThrottlingAction, mCompletionDurationMillis);
+        }
+    }
+
+}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 08b6eda..b785037 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -29,6 +29,7 @@
  * Define capability of a modem group. That is, the capabilities
  * are shared between those modems defined by list of modem IDs.
  *
+ * @hide
  */
 public final class PhoneCapability implements Parcelable {
     // Hardcoded default DSDS capability.
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 95c69ba..8d49e15 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -29,6 +29,10 @@
 import java.util.Arrays;
 import java.util.Objects;
 
+/**
+ * @hide
+ */
+@SystemApi
 public final class PhysicalChannelConfig implements Parcelable {
 
     // TODO(b/72993578) consolidate these enums in a central location.
@@ -82,7 +86,7 @@
     private int mFrequencyRange;
 
     /**
-     * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
+     * The absolute radio frequency channel number, {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
      */
     private int mChannelNumber;
 
@@ -93,7 +97,7 @@
     private int[] mContextIds;
 
     /**
-     * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN}
+     * The physical cell identifier for this cell - PCI, PSC, {@link PHYSICAL_CELL_ID_UNKNOWN}
      * if unknown.
      */
     private int mPhysicalCellId;
@@ -149,7 +153,7 @@
 
     /**
      * @return the absolute radio frequency channel number for this physical channel,
-     * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
+     * {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
      */
     public int getChannelNumber() {
         return mChannelNumber;
@@ -165,7 +169,7 @@
      * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
      * Reference: 3GPP TS 38.211 section 7.4.2.1.
      *
-     * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN}
+     * @return the physical cell identifier for this cell, {@link PHYSICAL_CELL_ID_UNKNOWN}
      * if {@link android.telephony.CellInfo#UNAVAILABLE}.
      */
     @IntRange(from = 0, to = 1007)
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
new file mode 100644
index 0000000..7c7eb9f
--- /dev/null
+++ b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import android.util.ArraySet;
+
+/**
+ * Contains the set of supported capabilities that the Radio Interface supports on this device.
+ *
+ * @hide
+ */
+public class RadioInterfaceCapabilities {
+
+    private final ArraySet<String> mSupportedCapabilities;
+
+
+    public RadioInterfaceCapabilities() {
+        mSupportedCapabilities = new ArraySet<>();
+    }
+
+    /**
+     * Marks a capability as supported
+     *
+     * @param capabilityName the name of the capability
+     */
+    public void addSupportedCapability(
+            @TelephonyManager.RadioInterfaceCapability String capabilityName) {
+        mSupportedCapabilities.add(capabilityName);
+    }
+
+    /**
+     * Whether the capability is supported
+     *
+     * @param capabilityName the name of the capability
+     */
+    public boolean isSupported(String capabilityName) {
+        return mSupportedCapabilities.contains(capabilityName);
+    }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 964cf76..572aed3 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -447,6 +447,9 @@
      *  <code>RESULT_RIL_NO_RESOURCES</code><br>
      *  <code>RESULT_RIL_CANCELLED</code><br>
      *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
@@ -561,6 +564,9 @@
      *  <code>RESULT_RIL_NO_RESOURCES</code><br>
      *  <code>RESULT_RIL_CANCELLED</code><br>
      *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
@@ -962,6 +968,9 @@
      *  <code>RESULT_RIL_NO_RESOURCES</code><br>
      *  <code>RESULT_RIL_CANCELLED</code><br>
      *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
@@ -1220,6 +1229,9 @@
      *  <code>RESULT_RIL_NO_RESOURCES</code><br>
      *  <code>RESULT_RIL_CANCELLED</code><br>
      *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
@@ -1419,6 +1431,9 @@
      *  <code>RESULT_RIL_NO_RESOURCES</code><br>
      *  <code>RESULT_RIL_CANCELLED</code><br>
      *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
@@ -2298,7 +2313,10 @@
             RESULT_RIL_OPERATION_NOT_ALLOWED,
             RESULT_RIL_NO_RESOURCES,
             RESULT_RIL_CANCELLED,
-            RESULT_RIL_SIM_ABSENT
+            RESULT_RIL_SIM_ABSENT,
+            RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
+            RESULT_RIL_ACCESS_BARRED,
+            RESULT_RIL_BLOCKED_DUE_TO_CALL
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Result {}
@@ -2563,6 +2581,21 @@
      */
     public static final int RESULT_RIL_SIM_ABSENT = 120;
 
+    /**
+     * 1X voice and SMS are not allowed simulteneously.
+     */
+    public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121;
+
+    /**
+     * Access is barred.
+     */
+    public static final int RESULT_RIL_ACCESS_BARRED = 122;
+
+    /**
+     * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity.
+     */
+    public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123;
+
     // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 83e63ef..904232b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -791,6 +791,13 @@
     public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED;
 
     /**
+     * Determines if the user has enabled cross SIM calling for this subscription.
+     *
+     * @hide
+     */
+    public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED;
+
+    /**
      * TelephonyProvider column name for whether a subscription is opportunistic, that is,
      * whether the network it connects to is limited in functionality or coverage.
      * For example, CBRS.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 683f200..e341e0c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -78,6 +78,7 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.ThermalMitigationResult;
 import android.telephony.Annotation.UiccAppType;
 import android.telephony.CallForwardingInfo.CallForwardingReason;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
@@ -123,7 +124,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -155,7 +155,6 @@
 public class TelephonyManager {
     private static final String TAG = "TelephonyManager";
 
-    private TelephonyRegistryManager mTelephonyRegistryMgr;
     /**
      * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and
      * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}.
@@ -5595,22 +5594,12 @@
      * @param events The telephony state(s) of interest to the listener,
      *               as a bitwise-OR combination of {@link PhoneStateListener}
      *               LISTEN_ flags.
-     * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
+     * @deprecated use {@link #listen(long, PhoneStateListener) instead due to the event number
+     *             limit increased to 64.
      */
     @Deprecated
     public void listen(PhoneStateListener listener, int events) {
-        boolean notifyNow = getITelephony() != null;
-        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
-        if (mTelephonyRegistryMgr != null) {
-            if (events != PhoneStateListener.LISTEN_NONE) {
-                mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(getSubId(),
-                        getOpPackageName(), getAttributionTag(), listener, events, notifyNow);
-            } else {
-                unregisterPhoneStateListener(listener);
-            }
-        } else {
-            throw new IllegalStateException("telephony service is null.");
-        }
+        listen(events, listener);
     }
 
     /**
@@ -5646,21 +5635,18 @@
      *               LISTEN_ flags.
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
-     * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
      */
-    @Deprecated
     public void listen(long events, @NonNull PhoneStateListener listener) {
-        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
-        if (mTelephonyRegistryMgr != null) {
-            if (events != PhoneStateListener.LISTEN_NONE) {
-                mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(getSubId(),
-                        getOpPackageName(), getAttributionTag(), listener,
-                        Long.valueOf(events).intValue(), getITelephony() != null);
-            } else {
-                unregisterPhoneStateListener(listener);
-            }
+        if (mContext == null) return;
+        boolean notifyNow = (getITelephony() != null);
+        TelephonyRegistryManager telephonyRegistry =
+                (TelephonyRegistryManager)
+                        mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistry != null) {
+            telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(),
+                    listener, events, notifyNow);
         } else {
-            throw new IllegalStateException("telephony service is null.");
+            Rlog.w(TAG, "telephony registry not ready.");
         }
     }
 
@@ -13215,6 +13201,37 @@
     }
 
     /**
+     * Get which bands the modem's background scan is acting on, specified by
+     * {@link #setSystemSelectionChannels}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NonNull List<RadioAccessSpecifier> getSystemSelectionChannels() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSystemSelectionChannels(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Verifies whether the input MCC/MNC and MVNO correspond to the current carrier.
      *
      * @param mccmnc the carrier's mccmnc that you want to match
@@ -14194,69 +14211,140 @@
         return Collections.emptyList();
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"},
+            value = {})
+    public @interface RadioInterfaceCapability {}
+
     /**
-     * Registers a listener object to receive notification of changes
-     * in specified telephony states.
-     * <p>
-     * To register a listener, pass a {@link PhoneStateListener} which implements
-     * interfaces of events. For example,
-     * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
-     * {@link PhoneStateListener.ServiceStateChangedListener}.
+     * Whether the device supports a given capability on the radio interface.
      *
-     * At registration, and when a specified telephony state changes, the telephony manager invokes
-     * the appropriate callback method on the listener object and passes the current (updated)
-     * values.
-     * <p>
+     * If the capability is not in the set of radio interface capabilities, false is returned.
      *
-     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
-     * applies to the given subId. Otherwise, applies to
-     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
-     * pass a separate listener object to each TelephonyManager object created with
-     * {@link #createForSubscriptionId}.
+     * @param capability the name of the capability to check for
+     * @return the availability of the capability
      *
-     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
-     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
-     * {@link SecurityException} will be thrown otherwise.
-     *
-     * This API should be used sparingly -- large numbers of listeners will cause system
-     * instability. If a process has registered too many listeners without unregistering them, it
-     * may encounter an {@link IllegalStateException} when trying to register more listeners.
-     *
-     * @param executor The executor of where the callback will execute.
-     * @param listener The {@link PhoneStateListener} object to register.
+     * @hide
      */
-    public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PhoneStateListener listener) {
-        if (executor == null || listener == null) {
-            throw new IllegalStateException("telephony service is null.");
+    public boolean isRadioInterfaceCapabilitySupported(
+            @NonNull @RadioInterfaceCapability String capability) {
+        try {
+            if (capability == null) return false;
+
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isRadioInterfaceCapabilitySupported(capability);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
-        mTelephonyRegistryMgr = (TelephonyRegistryManager)
-                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
-        if (mTelephonyRegistryMgr != null) {
-            mTelephonyRegistryMgr.registerPhoneStateListener(executor, getSubId(),
-                    getOpPackageName(), getAttributionTag(), listener, getITelephony() != null);
-        } else {
-            throw new IllegalStateException("telephony service is null.");
-        }
+        return false;
     }
 
     /**
-     * Unregister an existing {@link PhoneStateListener}.
+     * Indicates that the thermal mitigation request was completed successfully.
      *
-     * @param listener The {@link PhoneStateListener} object to unregister.
+     * @hide
      */
-    public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) {
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0;
 
-        if (mContext == null) {
-            throw new IllegalStateException("telephony service is null.");
-        }
+    /**
+     * Indicates that the thermal mitigation request was not completed because of a modem error.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1;
 
-        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
-        if (mTelephonyRegistryMgr != null) {
-            mTelephonyRegistryMgr.unregisterPhoneStateListener(getSubId(), getOpPackageName(),
-                    getAttributionTag(), listener, getITelephony() != null);
-        } else {
+    /**
+     * Indicates that the thermal mitigation request was not completed because the modem is not
+     * available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2;
+
+    /**
+     * Indicates that the thermal mitigation request could not power off the radio due to the device
+     * either being in an active voice call, device pending an emergency call, or any other state
+     * that would dissallow powering off of radio.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3;
+
+    /**
+     * Indicates that the thermal mitigation request resulted an unknown error.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4;
+
+    /**
+     * Thermal mitigation request to control functionalities at modem. Thermal mitigation is done
+     * per-subscription. Caller must be sure to bind the TelephonyManager instance to subId by
+     * calling {@link #createForSubscriptionId(int)} if they want thermal mitigation on a specific
+     * subscription Id. Otherwise, TelephonyManager will use the default subscription.
+     *
+     * Calling this does not guarantee that the thermal mitigation action requested was done to
+     * completion. A thermal module should actively monitor the temperature levels and request an
+     * appropriate thermal mitigation action. Every action is assumed to be done 'on top of' the
+     * previous action, where the order of actions from least thermal mitigation to most is as
+     * follows:
+     * <ol>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_DATA_THROTTLING}</li>
+     *   <ol>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}</li>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}</li>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}</li>
+     *   </ol>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY}</li>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}</li>
+     * </ol>
+     *
+     * So, for example, requesting {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will ensure that the
+     * data on secondary carrier has been disabled before throttling on primary carrier. {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} will ensure that data on both
+     * primary and secondary have been disabled. {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF} will ensure that voice is
+     * disabled and that data on both primary and secondary carriers are disabled before turning
+     * radio off. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD} is not part of the order
+     * and can be used at any time during data throttling to hold onto the current level of data
+     * throttling.
+     *
+     * @param thermalMitigationRequest Thermal mitigation request. See {@link
+     * ThermalMitigationRequest} for details.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @ThermalMitigationResult
+    public int sendThermalMitigationRequest(
+            @NonNull ThermalMitigationRequest thermalMitigationRequest) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest);
+            }
             throw new IllegalStateException("telephony service is null.");
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Telephony#thermalMitigationRequest RemoteException", ex);
+            ex.rethrowFromSystemServer();
         }
+        return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/telephony/java/android/telephony/ThermalMitigationRequest.aidl
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
copy to telephony/java/android/telephony/ThermalMitigationRequest.aidl
index d7a3af0..a912f77 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
+++ b/telephony/java/android/telephony/ThermalMitigationRequest.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,13 +11,9 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.classifier;
+package android.telephony;
 
-public class PointerCountEvaluator {
-    public static float evaluate(int value) {
-        return (value - 1) * (value - 1);
-    }
-}
+parcelable ThermalMitigationRequest;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.java b/telephony/java/android/telephony/ThermalMitigationRequest.java
new file mode 100644
index 0000000..91ad9c3
--- /dev/null
+++ b/telephony/java/android/telephony/ThermalMitigationRequest.java
@@ -0,0 +1,248 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Class stores information related to the type of data throttling request to be sent to {@link
+ * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}.
+ * @hide
+ */
+@SystemApi
+public final class ThermalMitigationRequest implements Parcelable {
+    /**
+     * Sent as a thermal mititgation action to {@link
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to start data
+     * throttling. {@link TelephonyManager#InvalidThermalMitigationRequestException} will be thrown
+     * if dataThrottlingRequest is {@code null} or if completion duration is < 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0;
+
+    /**
+     * Sent as a thermal mititgation action to {@link
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to allow only voice
+     * calls and internet data will not be available. This attempts to enable radio if currently
+     * disabled for thermal mitigation with no guarantee of it actually turning on.
+     * dataThrottlingRequest must be {@code null} or {@link
+     * TelephonyManager#InvalidThermalMitigationRequestException} will be thrown.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1;
+
+    /**
+     * Sent as a thermal mititgation action to {@link'
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to turn radio off. If
+     * radio is not able to be powered off because of an ongoing voice call, pending emergency call,
+     * or any other state that wouldn't allow radio off, {@link
+     * TelephonyManager#THERMAL_MITIGATION_RESULT_INVALID_STATE}.
+     * dataThrottlingRequest must be {@code null} or
+     * {@link TelephonyManager#InvalidThermalMitigationRequestException} will be returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2;
+
+    /**
+     * Type of thermal mitigation action.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "THERMAL_MITIGATION_ACTION_" }, value = {
+        THERMAL_MITIGATION_ACTION_DATA_THROTTLING,
+        THERMAL_MITIGATION_ACTION_VOICE_ONLY,
+        THERMAL_MITIGATION_ACTION_RADIO_OFF})
+    public @interface ThermalMitigationAction {}
+
+    private @ThermalMitigationAction int mThermalMitigationAction;
+    private DataThrottlingRequest mDataThrottlingRequest;
+
+    /**
+     * @param thermalMitigationAction thermal mitigation action.
+     * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. This
+     * is only applicable if thermalMitigationAction is
+     * {@link #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it must be set to
+     * {@code null}. See {@link DataThrottlingRequest} for more details.
+     */
+    private ThermalMitigationRequest(@ThermalMitigationAction int thermalMitigationAction,
+            @Nullable DataThrottlingRequest dataThrottlingRequest) {
+        mThermalMitigationAction = thermalMitigationAction;
+        mDataThrottlingRequest = dataThrottlingRequest;
+    }
+
+    private ThermalMitigationRequest(Parcel in) {
+        mThermalMitigationAction = in.readInt();
+        mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader());
+    }
+
+     /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mThermalMitigationAction);
+        dest.writeParcelable(mDataThrottlingRequest, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "[ThermalMitigationRequest "
+            + ", thermalMitigationAction=" + mThermalMitigationAction
+            + ", dataThrottlingRequest=" + mDataThrottlingRequest
+            + "]";
+    }
+
+    /**
+     * @return the thermal mitigation action.
+     */
+    public @ThermalMitigationAction int getThermalMitigationAction() {
+        return mThermalMitigationAction;
+    }
+
+    /**
+     * @return the data throttling request.
+     */
+    @Nullable
+    public DataThrottlingRequest getDataThrottlingRequest() {
+        return mDataThrottlingRequest;
+    }
+
+    public static final @NonNull Parcelable.Creator<ThermalMitigationRequest> CREATOR =
+            new Parcelable.Creator<ThermalMitigationRequest>() {
+
+        @Override
+        public ThermalMitigationRequest createFromParcel(Parcel in) {
+            return new ThermalMitigationRequest(in);
+        }
+
+        @Override
+        public ThermalMitigationRequest[] newArray(int size) {
+            return new ThermalMitigationRequest[size];
+        }
+    };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link ThermalMitigationRequest} when
+     * creating a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code ThermalMitigationRequest}:
+     *
+     * <pre><code>
+     *
+     * ThermalMitigationRequest dp = new ThermalMitigationRequest.Builder()
+     *     .setThermalMitigationAction(
+     *          ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+     *     .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+     *          .setDataThrottlingAction(
+     *              DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER)
+     *          .setCompletionDurationMillis(10000L)
+     *          .build())
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private @ThermalMitigationAction int mThermalMitigationAction = -1;
+        private DataThrottlingRequest mDataThrottlingRequest;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Set the thermal mitigation action.
+         *
+         * @param thermalMitigationAction thermal mitigation action. See {@link
+         *      #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}, {@link
+         *      #THERMAL_MITIGATION_ACTION_VOICE_ONLY}, and {@link
+         *      #THERMAL_MITIGATION_ACTION_RADIO_OFF} for more details.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setThermalMitigationAction(
+                @ThermalMitigationAction int thermalMitigationAction) {
+            mThermalMitigationAction = thermalMitigationAction;
+            return this;
+        }
+
+        /**
+         * Set the data throttling request.
+         *
+         * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling.
+         *      This is only applicable if thermalMitigationAction is {@link
+         *      #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it should not be set and
+         *      will throw an IllegalArgumentException if it is. See {@link DataThrottlingRequest}
+         *      for more details.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDataThrottlingRequest(
+                @NonNull DataThrottlingRequest dataThrottlingRequest) {
+            mDataThrottlingRequest = dataThrottlingRequest;
+            return this;
+        }
+
+        /**
+         * Build the ThermalMitigationRequest.
+         *
+         * @return the ThermalMitigationRequest object.
+         */
+        public @NonNull ThermalMitigationRequest build() {
+            if (mThermalMitigationAction < 0) {
+                throw new IllegalArgumentException("thermalMitigationAction was "
+                        + " not set");
+            }
+
+            if (mThermalMitigationAction == THERMAL_MITIGATION_ACTION_DATA_THROTTLING) {
+                if (mDataThrottlingRequest == null) {
+                    throw new IllegalArgumentException("dataThrottlingRequest  cannot be null for "
+                            + "THERMAL_MITIGATION_ACTION_DATA_THROTTLING");
+                }
+
+
+            } else if (mDataThrottlingRequest != null) {
+                throw new IllegalArgumentException("dataThrottlingRequest must be null for "
+                        + "THERMAL_MITIGATION_ACTION_VOICE_ONLY and "
+                        + "THERMAL_MITIGATION_ACTION_RADIO_OFF");
+            }
+
+            return new ThermalMitigationRequest(mThermalMitigationAction, mDataThrottlingRequest);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
similarity index 66%
rename from packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
rename to telephony/java/android/telephony/data/ApnThrottleStatus.aidl
index d7a3af0..46bc4ab 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
+++ b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,13 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.classifier;
+/** @hide */
+package android.telephony.data;
 
-public class PointerCountEvaluator {
-    public static float evaluate(int value) {
-        return (value - 1) * (value - 1);
-    }
-}
+parcelable ApnThrottleStatus;
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java
new file mode 100644
index 0000000..51461d1
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java
@@ -0,0 +1,383 @@
+/*
+ * 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.data;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation;
+
+import java.util.Objects;
+
+/**
+ * Status information regarding the throttle status of an APN type.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ApnThrottleStatus implements Parcelable {
+    /**
+     * The APN type is not throttled.
+     */
+    public static final int THROTTLE_TYPE_NONE = 1;
+
+    /**
+     * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()}
+     * has reached {@link ApnThrottleStatus#getThrottleExpiryTimeMillis}
+     */
+    public static final int THROTTLE_TYPE_ELAPSED_TIME = 2;
+
+    /** {@hide} */
+    @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = {
+            ApnThrottleStatus.THROTTLE_TYPE_NONE,
+            ApnThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
+    })
+    public @interface ThrottleType {
+    }
+
+    /**
+     * The framework will not retry the APN type.
+     */
+    public static final int RETRY_TYPE_NONE = 1;
+
+    /**
+     * The next time the framework retries, it will attempt to establish a new connection.
+     */
+    public static final int RETRY_TYPE_NEW_CONNECTION = 2;
+
+    /**
+     * The next time the framework retires, it will retry to handover.
+     */
+    public static final int RETRY_TYPE_HANDOVER = 3;
+
+    /** {@hide} */
+    @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = {
+            ApnThrottleStatus.RETRY_TYPE_NONE,
+            ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
+            ApnThrottleStatus.RETRY_TYPE_HANDOVER,
+    })
+    public @interface RetryType {
+    }
+
+    private final int mSlotIndex;
+    private final @AccessNetworkConstants.TransportType int mTransportType;
+    private final @Annotation.ApnType int mApnType;
+    private final long mThrottleExpiryTimeMillis;
+    private final @RetryType int mRetryType;
+    private final @ThrottleType int mThrottleType;
+
+    /**
+     * The slot index that the status applies to.
+     *
+     * @return the slot index
+     */
+    public int getSlotIndex() {
+        return mSlotIndex;
+    }
+
+    /**
+     * The type of transport that the status applies to.
+     *
+     * @return the transport type
+     */
+    @AccessNetworkConstants.TransportType
+    public int getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * The APN type that the status applies to.
+     *
+     * @return the apn type
+     */
+    @Annotation.ApnType
+    public int getApnType() {
+        return mApnType;
+    }
+
+    /**
+     * The type of throttle applied to the APN type.
+     *
+     * @return the throttle type
+     */
+    @ThrottleType
+    public int getThrottleType() {
+        return mThrottleType;
+    }
+
+    /**
+     * Indicates the type of request that the framework will make the next time it retries
+     * to call {@link IDataService#setupDataCall}.
+     *
+     * @return the retry type
+     */
+    @RetryType
+    public int getRetryType() {
+        return mRetryType;
+    }
+
+    /**
+     * Gets the time at which the throttle expires.  The value is based off of
+     * {@link SystemClock#elapsedRealtime}.
+     *
+     * This value only applies when the throttle type is set to
+     * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+     *
+     * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+     *
+     * @return the time at which the throttle expires
+     */
+    @ElapsedRealtimeLong
+    public long getThrottleExpiryTimeMillis() {
+        return mThrottleExpiryTimeMillis;
+    }
+
+    private ApnThrottleStatus(int slotIndex,
+            @AccessNetworkConstants.TransportType int transportType,
+            @Annotation.ApnType int apnTypes,
+            @ThrottleType int throttleType,
+            long throttleExpiryTimeMillis,
+            @RetryType int retryType) {
+        mSlotIndex = slotIndex;
+        mTransportType = transportType;
+        mApnType = apnTypes;
+        mThrottleType = throttleType;
+        mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+        mRetryType = retryType;
+    }
+
+    private ApnThrottleStatus(@NonNull Parcel source) {
+        mSlotIndex = source.readInt();
+        mTransportType = source.readInt();
+        mApnType = source.readInt();
+        mThrottleExpiryTimeMillis = source.readLong();
+        mRetryType = source.readInt();
+        mThrottleType = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSlotIndex);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mApnType);
+        dest.writeLong(mThrottleExpiryTimeMillis);
+        dest.writeInt(mRetryType);
+        dest.writeInt(mThrottleType);
+    }
+
+    public static final @NonNull Parcelable.Creator<ApnThrottleStatus> CREATOR =
+            new Parcelable.Creator<ApnThrottleStatus>() {
+                @Override
+                public ApnThrottleStatus createFromParcel(@NonNull Parcel source) {
+                    return new ApnThrottleStatus(source);
+                }
+
+                @Override
+                public ApnThrottleStatus[] newArray(int size) {
+                    return new ApnThrottleStatus[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType,
+                mThrottleExpiryTimeMillis, mTransportType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        } else if (obj instanceof ApnThrottleStatus) {
+            ApnThrottleStatus other = (ApnThrottleStatus) obj;
+            return this.mSlotIndex == other.mSlotIndex
+                    && this.mApnType == other.mApnType
+                    && this.mRetryType == other.mRetryType
+                    && this.mThrottleType == other.mThrottleType
+                    && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis
+                    && this.mTransportType == other.mTransportType;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ApnThrottleStatus{"
+                + "mSlotIndex=" + mSlotIndex
+                + ", mTransportType=" + mTransportType
+                + ", mApnType=" + ApnSetting.getApnTypeString(mApnType)
+                + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis
+                + ", mRetryType=" + mRetryType
+                + ", mThrottleType=" + mThrottleType
+                + '}';
+    }
+
+    /**
+     * Provides a convenient way to set the fields of an {@link ApnThrottleStatus} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code ApnThrottleStatus}:
+     *
+     * <pre><code>
+     *
+     * DataCallResponseApnThrottleStatus = new ApnThrottleStatus.Builder()
+     *     .setSlotIndex(1)
+     *     .setApnType({@link ApnSetting#TYPE_EMERGENCY})
+     *     .setNoThrottle()
+     *     .setRetryType({@link ApnThrottleStatus#RETRY_TYPE_NEW_CONNECTION})
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private int mSlotIndex;
+        private @AccessNetworkConstants.TransportType int mTransportType;
+        private @Annotation.ApnType int mApnType;
+        private long mThrottleExpiryTimeMillis;
+        private @RetryType int mRetryType;
+        private @ThrottleType int mThrottleType;
+        public static final long NO_THROTTLE_EXPIRY_TIME =
+                DataCallResponse.RETRY_DURATION_UNDEFINED;
+
+        /**
+         * Default constructor for the Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the slot index.
+         *
+         * @param slotIndex the slot index.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSlotIndex(int slotIndex) {
+            this.mSlotIndex = slotIndex;
+            return this;
+        }
+
+        /**
+         * Set the transport type.
+         *
+         * @param transportType the transport type.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setTransportType(@AccessNetworkConstants.TransportType
+                int transportType) {
+            this.mTransportType = transportType;
+            return this;
+        }
+
+        /**
+         * Set the APN type.
+         *
+         * @param apnType  the APN type.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setApnType(@Annotation.ApnType int apnType) {
+            this.mApnType = apnType;
+            return this;
+        }
+
+        /**
+         * Sets the time at which the throttle will expire.  The value is based off of
+         * {@link SystemClock#elapsedRealtime}.
+         *
+         * When setting this value, the throttle type is set to
+         * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+         *
+         * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+         *
+         * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires.
+         *                                 Throws {@link IllegalArgumentException} for values less
+         *                                 than 0.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setThrottleExpiryTimeMillis(
+                @ElapsedRealtimeLong long throttleExpiryTimeMillis) {
+            if (throttleExpiryTimeMillis >= 0) {
+                this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+                this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME;
+            } else {
+                throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than "
+                        + "or equal to 0");
+            }
+            return this;
+        }
+
+        /**
+         * Sets the status of the APN type as not being throttled.
+         *
+         * When setting this value, the throttle type is set to
+         * {@link ApnThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to
+         * {@link Builder#NO_THROTTLE_EXPIRY_TIME}.
+         *
+         * @return The same instance of the builder.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setNoThrottle() {
+            mThrottleType = THROTTLE_TYPE_NONE;
+            mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME;
+            return this;
+        }
+
+        /**
+         * Set the type of request that the framework will make the next time it retries
+         * to call {@link IDataService#setupDataCall}.
+         *
+         * @param retryType the type of request
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setRetryType(@RetryType int retryType) {
+            this.mRetryType = retryType;
+            return this;
+        }
+
+        /**
+         * Build the {@link ApnThrottleStatus}
+         *
+         * @return the {@link ApnThrottleStatus} object
+         */
+        @NonNull
+        public ApnThrottleStatus build() {
+            return new ApnThrottleStatus(
+                    mSlotIndex,
+                    mTransportType,
+                    mApnType,
+                    mThrottleType,
+                    mThrottleExpiryTimeMillis,
+                    mRetryType);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index aae7713..090c970 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -135,6 +135,8 @@
     private final int mMtuV6;
     private final @HandoverFailureMode int mHandoverFailureMode;
     private final int mPduSessionId;
+    private final Qos mDefaultQos;
+    private final List<QosSession> mQosSessions;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -183,6 +185,8 @@
         mMtu = mMtuV4 = mMtuV6 = mtu;
         mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
         mPduSessionId = PDU_SESSION_ID_NOT_SET;
+        mDefaultQos = null;
+        mQosSessions = new ArrayList<>();
     }
 
     private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -190,7 +194,8 @@
             @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
             @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
-            @HandoverFailureMode int handoverFailureMode, int pduSessionId) {
+            @HandoverFailureMode int handoverFailureMode, int pduSessionId,
+            @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -210,6 +215,8 @@
         mMtuV6 = mtuV6;
         mHandoverFailureMode = handoverFailureMode;
         mPduSessionId = pduSessionId;
+        mDefaultQos = defaultQos;
+        mQosSessions = qosSessions;
     }
 
     /** @hide */
@@ -234,6 +241,9 @@
         mMtuV6 = source.readInt();
         mHandoverFailureMode = source.readInt();
         mPduSessionId = source.readInt();
+        mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
+        mQosSessions = new ArrayList<>();
+        source.readList(mQosSessions, QosSession.class.getClassLoader());
     }
 
     /**
@@ -357,6 +367,28 @@
         return mPduSessionId;
     }
 
+    /**
+     * @return default QOS of the data call received from the network
+     *
+     * @hide
+     */
+
+    @Nullable
+    public Qos getDefaultQos() {
+        return mDefaultQos;
+    }
+
+    /**
+     * @return All the dedicated bearer QOS sessions of the data call received from the network
+     *
+     * @hide
+     */
+
+    @NonNull
+    public List<QosSession> getQosSessions() {
+        return mQosSessions;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -377,6 +409,8 @@
            .append(" mtuV6=").append(getMtuV6())
            .append(" handoverFailureMode=").append(getHandoverFailureMode())
            .append(" pduSessionId=").append(getPduSessionId())
+           .append(" defaultQos=").append(mDefaultQos)
+           .append(" qosSessions=").append(mQosSessions)
            .append("}");
         return sb.toString();
     }
@@ -390,12 +424,22 @@
         }
 
         DataCallResponse other = (DataCallResponse) o;
-        return this.mCause == other.mCause
-                && this.mSuggestedRetryTime == other.mSuggestedRetryTime
-                && this.mId == other.mId
-                && this.mLinkStatus == other.mLinkStatus
-                && this.mProtocolType == other.mProtocolType
-                && this.mInterfaceName.equals(other.mInterfaceName)
+
+        final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ?
+                mDefaultQos == other.mDefaultQos :
+                mDefaultQos.equals(other.mDefaultQos);
+
+        final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
+                mQosSessions == other.mQosSessions :
+                mQosSessions.size() == other.mQosSessions.size()
+                && mQosSessions.containsAll(other.mQosSessions);
+
+        return mCause == other.mCause
+                && mSuggestedRetryTime == other.mSuggestedRetryTime
+                && mId == other.mId
+                && mLinkStatus == other.mLinkStatus
+                && mProtocolType == other.mProtocolType
+                && mInterfaceName.equals(other.mInterfaceName)
                 && mAddresses.size() == other.mAddresses.size()
                 && mAddresses.containsAll(other.mAddresses)
                 && mDnsAddresses.size() == other.mDnsAddresses.size()
@@ -408,14 +452,17 @@
                 && mMtuV4 == other.mMtuV4
                 && mMtuV6 == other.mMtuV6
                 && mHandoverFailureMode == other.mHandoverFailureMode
-                && mPduSessionId == other.mPduSessionId;
+                && mPduSessionId == other.mPduSessionId
+                && isQosSame
+                && isQosSessionsSame;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
-                mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+                mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
+                mQosSessions);
     }
 
     @Override
@@ -440,6 +487,16 @@
         dest.writeInt(mMtuV6);
         dest.writeInt(mHandoverFailureMode);
         dest.writeInt(mPduSessionId);
+        if (mDefaultQos != null) {
+            if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) {
+                dest.writeParcelable((EpsQos) mDefaultQos, flags);
+            } else {
+                dest.writeParcelable((NrQos) mDefaultQos, flags);
+            }
+        } else {
+            dest.writeParcelable(null, flags);
+        }
+        dest.writeList(mQosSessions);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -519,6 +576,10 @@
 
         private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
 
+        private Qos mDefaultQos;
+
+        private List<QosSession> mQosSessions = new ArrayList<>();
+
         /**
          * Default constructor for Builder.
          */
@@ -713,6 +774,35 @@
         }
 
         /**
+         * Set the default QOS for this data connection.
+         *
+         * @param defaultQos QOS (Quality Of Service) received from network.
+         *
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) {
+            mDefaultQos = defaultQos;
+            return this;
+        }
+
+        /**
+         * Set the dedicated bearer QOS sessions for this data connection.
+         *
+         * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+         * from network.
+         *
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
+            mQosSessions = qosSessions;
+            return this;
+        }
+
+        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
@@ -720,7 +810,8 @@
         public @NonNull DataCallResponse build() {
             return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                     mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+                    mDefaultQos, mQosSessions);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 7768597..2ec9651 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -107,6 +107,9 @@
     private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 11;
     private static final int DATA_SERVICE_REQUEST_START_HANDOVER                       = 12;
     private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER                      = 13;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED             = 14;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED           = 15;
+    private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED                   = 16;
 
     private final HandlerThread mHandlerThread;
 
@@ -129,6 +132,8 @@
 
         private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
 
+        private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>();
+
         /**
          * Constructor
          * @param slotIndex SIM slot index the data service provider associated with.
@@ -326,6 +331,19 @@
             }
         }
 
+        private void registerForApnUnthrottled(IDataServiceCallback callback) {
+            synchronized (mApnUnthrottledCallbacks) {
+                mApnUnthrottledCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForApnUnthrottled(IDataServiceCallback callback) {
+            synchronized (mApnUnthrottledCallbacks) {
+                mApnUnthrottledCallbacks.remove(callback);
+            }
+        }
+
+
         /**
          * Notify the system that current data call list changed. Data service must invoke this
          * method whenever there is any data call status changed.
@@ -343,6 +361,21 @@
         }
 
         /**
+         * Notify the system that a given APN was unthrottled.
+         *
+         * @param apn Access Point Name defined by the carrier.
+         */
+        public final void notifyApnUnthrottled(@NonNull String apn) {
+            synchronized (mApnUnthrottledCallbacks) {
+                for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+                            mSlotIndex, 0, new ApnUnthrottledIndication(apn,
+                                    callback)).sendToTarget();
+                }
+            }
+        }
+
+        /**
          * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
          * or when the data service provider is removed. The extended class should implement this
          * method to perform cleanup works.
@@ -429,6 +462,16 @@
         }
     }
 
+    private static final class ApnUnthrottledIndication {
+        public final String apn;
+        public final IDataServiceCallback callback;
+        ApnUnthrottledIndication(String apn,
+                IDataServiceCallback callback) {
+            this.apn = apn;
+            this.callback = callback;
+        }
+    }
+
     private class DataServiceHandler extends Handler {
 
         DataServiceHandler(Looper looper) {
@@ -544,6 +587,26 @@
                             (cReq.callback != null)
                                     ? new DataServiceCallback(cReq.callback) : null);
                     break;
+                case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj);
+                    break;
+                case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    callback = (IDataServiceCallback) message.obj;
+                    serviceProvider.unregisterForApnUnthrottled(callback);
+                    break;
+                case DATA_SERVICE_INDICATION_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    ApnUnthrottledIndication apnUnthrottledIndication =
+                            (ApnUnthrottledIndication) message.obj;
+                    try {
+                        apnUnthrottledIndication.callback
+                                .onApnUnthrottled(apnUnthrottledIndication.apn);
+                    } catch (RemoteException e) {
+                        loge("Failed to call onApnUnthrottled. " + e);
+                    }
+                    break;
             }
         }
     }
@@ -695,6 +758,26 @@
             mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER,
                     slotIndex, 0, req).sendToTarget();
         }
+
+        @Override
+        public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("registerForUnthrottleApn: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("uregisterForUnthrottleApn: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED,
+                    slotIndex, 0, callback).sendToTarget();
+        }
     }
 
     private void log(String s) {
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index eef0e01..52bf15f 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -233,7 +233,7 @@
      */
     @NonNull
     public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) {
-        switch(resultCode) {
+        switch (resultCode) {
             case RESULT_SUCCESS:
                 return "RESULT_SUCCESS";
             case RESULT_ERROR_UNSUPPORTED:
@@ -248,4 +248,22 @@
                 return "Missing case for result code=" + resultCode;
         }
     }
+
+    /**
+     * Indicates that the specified APN is no longer throttled.
+     *
+     * @param apn Access Point Name defined by the carrier.
+     */
+    public void onApnUnthrottled(@NonNull String apn) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onApnUnthrottled");
+                mCallback.onApnUnthrottled(apn);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "onApnUnthrottled: remote exception", e);
+            }
+        } else {
+            Rlog.e(TAG, "onApnUnthrottled: callback is null!");
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
new file mode 100644
index 0000000..ad43068
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -0,0 +1,105 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class EpsQos extends Qos implements Parcelable {
+
+    int qosClassId;
+
+    public EpsQos() {
+        super(Qos.QOS_TYPE_EPS,
+                new android.hardware.radio.V1_6.QosBandwidth(),
+                new android.hardware.radio.V1_6.QosBandwidth());
+    }
+
+    public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) {
+        super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink);
+        qosClassId = qos.qci;
+    }
+
+    private EpsQos(Parcel source) {
+        super(source);
+        qosClassId = source.readInt();
+    }
+
+    public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
+        return new EpsQos(in);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags);
+        dest.writeInt(qosClassId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), qosClassId);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof EpsQos)) {
+            return false;
+        }
+
+        EpsQos other = (EpsQos) o;
+
+        return this.qosClassId == other.qosClassId
+               && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return "EpsQos {"
+                + " qosClassId=" + qosClassId
+                + " downlink=" + downlink
+                + " uplink=" + uplink + "}";
+    }
+
+    public static final @NonNull Parcelable.Creator<EpsQos> CREATOR =
+            new Parcelable.Creator<EpsQos>() {
+                @Override
+                public EpsQos createFromParcel(Parcel source) {
+                    return new EpsQos(source);
+                }
+
+                @Override
+                public EpsQos[] newArray(int size) {
+                    return new EpsQos[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 33226fe..3f1f033 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -40,4 +40,6 @@
     void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback);
     void startHandover(int slotId, int cid, IDataServiceCallback callback);
     void cancelHandover(int slotId, int cid, IDataServiceCallback callback);
+    void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
+    void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
 }
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index d296e7b..9cc2fea 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -32,4 +32,5 @@
     void onDataCallListChanged(in List<DataCallResponse> dataCallList);
     void onHandoverStarted(int result);
     void onHandoverCancelled(int result);
+    void onApnUnthrottled(in String apn);
 }
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
index 3bf09bc..2904082 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
@@ -17,6 +17,7 @@
 package android.telephony.data;
 
 import android.telephony.data.IQualifiedNetworksServiceCallback;
+import android.telephony.data.ApnThrottleStatus;
 
 /**
  * {@hide}
@@ -25,4 +26,5 @@
 {
     oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback);
     oneway void removeNetworkAvailabilityProvider(int slotId);
+    oneway void reportApnThrottleStatusChanged(int slotId, in List<ApnThrottleStatus> statuses);
 }
diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java
new file mode 100644
index 0000000..2011eed
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQos.java
@@ -0,0 +1,112 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class NrQos extends Qos implements Parcelable {
+    int qosFlowId;
+    int fiveQi;
+    int averagingWindowMs;
+
+    public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) {
+        super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink);
+        fiveQi = qos.fiveQi;
+        qosFlowId = qos.qfi;
+        averagingWindowMs = qos.averagingWindowMs;
+    }
+
+    private NrQos(Parcel source) {
+        super(source);
+        this.qosFlowId = source.readInt();
+        this.fiveQi = source.readInt();
+        this.averagingWindowMs = source.readInt();
+    }
+
+    public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) {
+        return new NrQos(in);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags);
+        dest.writeInt(qosFlowId);
+        dest.writeInt(fiveQi);
+        dest.writeInt(averagingWindowMs);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof NrQos)) {
+            return false;
+        }
+
+        NrQos other = (NrQos) o;
+
+        if (!super.equals(other)) {
+            return false;
+        }
+
+        return this.qosFlowId == other.qosFlowId
+            && this.fiveQi == other.fiveQi
+            && this.averagingWindowMs == other.averagingWindowMs;
+    }
+
+    @Override
+    public String toString() {
+        return "NrQos {"
+                + " fiveQi=" + fiveQi
+                + " downlink=" + downlink
+                + " uplink=" + uplink
+                + " qosFlowId=" + qosFlowId
+                + " averagingWindowMs=" + averagingWindowMs + "}";
+    }
+
+    public static final @NonNull Parcelable.Creator<NrQos> CREATOR =
+            new Parcelable.Creator<NrQos>() {
+                @Override
+                public NrQos createFromParcel(Parcel source) {
+                    return new NrQos(source);
+                }
+
+                @Override
+                public NrQos[] newArray(int size) {
+                    return new NrQos[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
new file mode 100644
index 0000000..c8bb91e
--- /dev/null
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -0,0 +1,175 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to QOS.
+ *
+ * @hide
+ */
+public abstract class Qos {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "QOS_TYPE_",
+            value = {QOS_TYPE_EPS, QOS_TYPE_NR})
+    public @interface QosType {}
+
+    @QosType
+    final int type;
+
+    static final int QOS_TYPE_EPS = 1;
+    static final int QOS_TYPE_NR = 2;
+
+    final QosBandwidth downlink;
+    final QosBandwidth uplink;
+
+    Qos(int type,
+            @NonNull android.hardware.radio.V1_6.QosBandwidth downlink,
+            @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) {
+        this.type = type;
+        this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps);
+        this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
+    }
+
+    static class QosBandwidth implements Parcelable {
+        int maxBitrateKbps;
+        int guaranteedBitrateKbps;
+
+        QosBandwidth() {
+        }
+
+        QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) {
+            this.maxBitrateKbps = maxBitrateKbps;
+            this.guaranteedBitrateKbps = guaranteedBitrateKbps;
+        }
+
+        private QosBandwidth(Parcel source) {
+            maxBitrateKbps = source.readInt();
+            guaranteedBitrateKbps = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(maxBitrateKbps);
+            dest.writeInt(guaranteedBitrateKbps);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+
+            if (o == null || !(o instanceof QosBandwidth)) {
+                return false;
+            }
+
+            QosBandwidth other = (QosBandwidth) o;
+            return maxBitrateKbps == other.maxBitrateKbps
+                    && guaranteedBitrateKbps == other.guaranteedBitrateKbps;
+        }
+
+        @Override
+        public String toString() {
+            return "Bandwidth {"
+                    + " maxBitrateKbps=" + maxBitrateKbps
+                    + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}";
+        }
+
+        public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR =
+                new Parcelable.Creator<QosBandwidth>() {
+                    @Override
+                    public QosBandwidth createFromParcel(Parcel source) {
+                        return new QosBandwidth(source);
+                    }
+
+                    @Override
+                    public QosBandwidth[] newArray(int size) {
+                        return new QosBandwidth[size];
+                    }
+                };
+    };
+
+    protected Qos(@NonNull Parcel source) {
+        type = source.readInt();
+        downlink = source.readParcelable(QosBandwidth.class.getClassLoader());
+        uplink = source.readParcelable(QosBandwidth.class.getClassLoader());
+    }
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    @CallSuper
+    public void writeToParcel(@QosType int type, Parcel dest, int flags) {
+        dest.writeInt(type);
+        dest.writeParcelable(downlink, flags);
+        dest.writeParcelable(uplink, flags);
+    }
+
+    /** @hide */
+    public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) {
+        switch (qos.getDiscriminator()) {
+            case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps:
+                  return new EpsQos(qos.eps());
+            case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr:
+                  return new NrQos(qos.nr());
+            default:
+                  return null;
+        }
+    }
+
+    /** @hide */
+    public @QosType int getType() {
+        return type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(downlink, uplink);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        Qos other = (Qos) o;
+        return type == other.type
+                && downlink.equals(other.downlink)
+                && uplink.equals(other.uplink);
+    }
+}
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosFilter.java
new file mode 100644
index 0000000..6927744
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosFilter.java
@@ -0,0 +1,373 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores QOS filter parameters as defined in
+ * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13.
+ *
+ * @hide
+ */
+public final class QosFilter implements Parcelable {
+
+    private List<LinkAddress> localAddresses;
+    private List<LinkAddress> remoteAddresses;
+    private PortRange localPort;
+    private PortRange remotePort;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "QOS_PROTOCOL_",
+            value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP,
+                    QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH})
+    public @interface QosProtocol {}
+
+    public static final int QOS_PROTOCOL_UNSPECIFIED =
+            android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED;
+    public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP;
+    public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP;
+    public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP;
+    public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH;
+
+    @QosProtocol
+    private int protocol;
+
+    private int typeOfServiceMask;
+
+    private long flowLabel;
+
+    /** IPSec security parameter index */
+    private long securityParameterIndex;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "QOS_FILTER_DIRECTION_",
+            value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
+                    QOS_FILTER_DIRECTION_BIDIRECTIONAL})
+    public @interface QosFilterDirection {}
+
+    public static final int QOS_FILTER_DIRECTION_DOWNLINK =
+            android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
+    public static final int QOS_FILTER_DIRECTION_UPLINK =
+            android.hardware.radio.V1_6.QosFilterDirection.UPLINK;
+    public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
+            android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
+
+    @QosFilterDirection
+    private int filterDirection;
+
+    /**
+     * Specified the order in which the filter needs to be matched.
+     * A Lower numerical value has a higher precedence.
+     */
+    private int precedence;
+
+    QosFilter() {
+        localAddresses = new ArrayList<>();
+        remoteAddresses = new ArrayList<>();
+        localPort = new PortRange();
+        remotePort = new PortRange();
+        protocol = QOS_PROTOCOL_UNSPECIFIED;
+        filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
+    }
+
+    public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+            PortRange localPort, PortRange remotePort, int protocol, int tos,
+            long flowLabel, long spi, int direction, int precedence) {
+        this.localAddresses = localAddresses;
+        this.remoteAddresses = remoteAddresses;
+        this.localPort = localPort;
+        this.remotePort = remotePort;
+        this.protocol = protocol;
+        this.typeOfServiceMask = tos;
+        this.flowLabel = flowLabel;
+        this.securityParameterIndex = spi;
+        this.filterDirection = direction;
+        this.precedence = precedence;
+    }
+
+    /** @hide */
+    public static @NonNull QosFilter create(
+            @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
+        QosFilter ret = new QosFilter();
+
+        String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
+        if (localAddresses != null) {
+            for (String address : localAddresses) {
+                ret.localAddresses.add(createLinkAddressFromString(address));
+            }
+        }
+
+        String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new);
+        if (remoteAddresses != null) {
+            for (String address : remoteAddresses) {
+                ret.remoteAddresses.add(createLinkAddressFromString(address));
+            }
+        }
+
+        if (qosFilter.localPort != null) {
+            if (qosFilter.localPort.getDiscriminator()
+                    == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
+                final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range();
+                ret.localPort.start = portRange.start;
+                ret.localPort.end = portRange.end;
+            }
+        }
+
+        if (qosFilter.remotePort != null) {
+            if (qosFilter.remotePort.getDiscriminator()
+                    == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
+                final android.hardware.radio.V1_6.PortRange portRange
+                        = qosFilter.remotePort.range();
+                ret.remotePort.start = portRange.start;
+                ret.remotePort.end = portRange.end;
+            }
+        }
+
+        ret.protocol = qosFilter.protocol;
+
+        if (qosFilter.tos != null) {
+            if (qosFilter.tos.getDiscriminator()
+                == android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) {
+                ret.typeOfServiceMask = qosFilter.tos.value();
+            }
+        }
+
+        if (qosFilter.flowLabel != null) {
+            if (qosFilter.flowLabel.getDiscriminator()
+                == android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) {
+                ret.flowLabel = qosFilter.flowLabel.value();
+            }
+        }
+
+        if (qosFilter.spi != null) {
+            if (qosFilter.spi.getDiscriminator()
+                == android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) {
+                ret.securityParameterIndex = qosFilter.spi.value();
+            }
+        }
+
+        ret.filterDirection = qosFilter.direction;
+        ret.precedence = qosFilter.precedence;
+
+        return ret;
+    }
+
+    public static class PortRange implements Parcelable {
+        int start;
+        int end;
+
+        PortRange() {
+            start = -1;
+            end = -1;
+        }
+
+        private PortRange(Parcel source) {
+            start = source.readInt();
+            end = source.readInt();
+        }
+
+        public PortRange(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(start);
+            dest.writeInt(end);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final @NonNull Parcelable.Creator<PortRange> CREATOR =
+                new Parcelable.Creator<PortRange>() {
+                    @Override
+                    public PortRange createFromParcel(Parcel source) {
+                        return new PortRange(source);
+                    }
+
+                    @Override
+                    public PortRange[] newArray(int size) {
+                        return new PortRange[size];
+                    }
+                };
+
+        @Override
+        public String toString() {
+            return "PortRange {"
+                    + " start=" + start
+                    + " end=" + end + "}";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+
+            if (o == null || !(o instanceof PortRange)) {
+              return false;
+            }
+
+            PortRange other = (PortRange) o;
+            return start == other.start
+                    && end == other.end;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(start, end);
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "QosFilter {"
+                + " localAddresses=" + localAddresses
+                + " remoteAddresses=" + remoteAddresses
+                + " localPort=" + localPort
+                + " remotePort=" + remotePort
+                + " protocol=" + protocol
+                + " typeOfServiceMask=" + typeOfServiceMask
+                + " flowLabel=" + flowLabel
+                + " securityParameterIndex=" + securityParameterIndex
+                + " filterDirection=" + filterDirection
+                + " precedence=" + precedence + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(localAddresses, remoteAddresses, localPort,
+                remotePort, protocol, typeOfServiceMask, flowLabel,
+                securityParameterIndex, filterDirection, precedence);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof QosFilter)) {
+            return false;
+        }
+
+        QosFilter other = (QosFilter) o;
+
+        return localAddresses.size() == other.localAddresses.size()
+                && localAddresses.containsAll(other.localAddresses)
+                && remoteAddresses.size() == other.remoteAddresses.size()
+                && remoteAddresses.containsAll(other.remoteAddresses)
+                && localPort.equals(other.localPort)
+                && remotePort.equals(other.remotePort)
+                && protocol == other.protocol
+                && typeOfServiceMask == other.typeOfServiceMask
+                && flowLabel == other.flowLabel
+                && securityParameterIndex == other.securityParameterIndex
+                && filterDirection == other.filterDirection
+                && precedence == other.precedence;
+    }
+
+    private static LinkAddress createLinkAddressFromString(String addressString) {
+        addressString = addressString.trim();
+        InetAddress address = null;
+        int prefixLength = -1;
+        try {
+            String[] pieces = addressString.split("/", 2);
+            address = InetAddresses.parseNumericAddress(pieces[0]);
+            if (pieces.length == 1) {
+                prefixLength = (address instanceof Inet4Address) ? 32 : 128;
+            } else if (pieces.length == 2) {
+                prefixLength = Integer.parseInt(pieces[1]);
+            }
+        } catch (NullPointerException e) {            // Null string.
+        } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
+        } catch (NumberFormatException e) {           // Non-numeric prefix.
+        } catch (IllegalArgumentException e) {        // Invalid IP address.
+        }
+
+        if (address == null || prefixLength == -1) {
+            throw new IllegalArgumentException("Invalid link address " + addressString);
+        }
+
+        return new LinkAddress(address, prefixLength, 0, 0,
+                LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
+    }
+
+    private QosFilter(Parcel source) {
+        localAddresses = new ArrayList<>();
+        source.readList(localAddresses, LinkAddress.class.getClassLoader());
+        remoteAddresses = new ArrayList<>();
+        source.readList(remoteAddresses, LinkAddress.class.getClassLoader());
+        localPort = source.readParcelable(PortRange.class.getClassLoader());
+        remotePort = source.readParcelable(PortRange.class.getClassLoader());
+        protocol = source.readInt();
+        typeOfServiceMask = source.readInt();
+        flowLabel = source.readLong();
+        securityParameterIndex = source.readLong();
+        filterDirection = source.readInt();
+        precedence = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeList(localAddresses);
+        dest.writeList(remoteAddresses);
+        dest.writeParcelable(localPort, flags);
+        dest.writeParcelable(remotePort, flags);
+        dest.writeInt(protocol);
+        dest.writeInt(typeOfServiceMask);
+        dest.writeLong(flowLabel);
+        dest.writeLong(securityParameterIndex);
+        dest.writeInt(filterDirection);
+        dest.writeInt(precedence);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
+            new Parcelable.Creator<QosFilter>() {
+                @Override
+                public QosFilter createFromParcel(Parcel source) {
+                    return new QosFilter(source);
+                }
+
+                @Override
+                public QosFilter[] newArray(int size) {
+                    return new QosFilter[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
new file mode 100644
index 0000000..f07b6a9
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosSession.java
@@ -0,0 +1,125 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosSession implements Parcelable{
+
+    final int qosSessionId;
+    final Qos qos;
+    final List<QosFilter> qosFilterList;
+
+    public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
+        this.qosSessionId = qosSessionId;
+        this.qos = qos;
+        this.qosFilterList = qosFilterList;
+    }
+
+    private QosSession(Parcel source) {
+        qosSessionId = source.readInt();
+        qos = source.readParcelable(Qos.class.getClassLoader());
+        qosFilterList = new ArrayList<>();
+        source.readList(qosFilterList, QosFilter.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(qosSessionId);
+        if (qos.getType() == Qos.QOS_TYPE_EPS) {
+            dest.writeParcelable((EpsQos)qos, flags);
+        } else {
+            dest.writeParcelable((NrQos)qos, flags);
+        }
+        dest.writeList(qosFilterList);
+    }
+
+    public static @NonNull QosSession create(
+            @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+        List<QosFilter> qosFilters = new ArrayList<>();
+
+        if (qosSession.qosFilters != null) {
+            for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+                qosFilters.add(QosFilter.create(filter));
+            }
+        }
+
+        return new QosSession(
+                        qosSession.qosSessionId,
+                        Qos.create(qosSession.qos),
+                        qosFilters);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "QosSession {"
+                + " qosSessionId=" + qosSessionId
+                + " qos=" + qos
+                + " qosFilterList=" + qosFilterList + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(qosSessionId, qos, qosFilterList);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof QosSession)) {
+            return false;
+        }
+
+        QosSession other = (QosSession) o;
+        return this.qosSessionId == other.qosSessionId
+                && this.qos.equals(other.qos)
+                && this.qosFilterList.size() == other.qosFilterList.size()
+                && this.qosFilterList.containsAll(other.qosFilterList);
+    }
+
+
+    public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
+            new Parcelable.Creator<QosSession>() {
+                @Override
+                public QosSession createFromParcel(Parcel source) {
+                    return new QosSession(source);
+                }
+
+                @Override
+                public QosSession[] newArray(int size) {
+                    return new QosSession[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 05971c4..4af63b4 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.Annotation.ApnType;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -65,6 +66,7 @@
     private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER               = 2;
     private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS          = 3;
     private static final int QNS_UPDATE_QUALIFIED_NETWORKS                          = 4;
+    private static final int QNS_APN_THROTTLE_STATUS_CHANGED                        = 5;
 
     private final HandlerThread mHandlerThread;
 
@@ -160,6 +162,17 @@
         }
 
         /**
+         * The framework calls this method when the throttle status of an APN changes.
+         *
+         * This method is meant to be overridden.
+         *
+         * @param statuses the statuses that have changed
+         */
+        public void reportApnThrottleStatusChanged(@NonNull List<ApnThrottleStatus> statuses) {
+            Log.d(TAG, "reportApnThrottleStatusChanged: statuses size=" + statuses.size());
+        }
+
+        /**
          * Called when the qualified networks provider is removed. The extended class should
          * implement this method to perform cleanup works.
          */
@@ -197,6 +210,12 @@
                                 + slotIndex);
                     }
                     break;
+                case QNS_APN_THROTTLE_STATUS_CHANGED:
+                    if (provider != null) {
+                        List<ApnThrottleStatus> statuses = (List<ApnThrottleStatus>) message.obj;
+                        provider.reportApnThrottleStatusChanged(statuses);
+                    }
+                    break;
 
                 case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER:
                     if (provider != null) {
@@ -286,6 +305,13 @@
             mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0)
                     .sendToTarget();
         }
+
+        @Override
+        public void reportApnThrottleStatusChanged(int slotIndex,
+                List<ApnThrottleStatus> statuses) {
+            mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses)
+                    .sendToTarget();
+        }
     }
 
     private void log(String s) {
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
index f384901..73d0840 100644
--- a/telephony/java/android/telephony/ims/DelegateRequest.java
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -98,4 +98,9 @@
     public int hashCode() {
         return Objects.hash(mFeatureTags);
     }
+
+    @Override
+    public String toString() {
+        return "DelegateRequest{mFeatureTags=" + mFeatureTags + '}';
+    }
 }
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 337b7d4..190a792 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -236,17 +236,17 @@
     public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
 
     /**
-     * The SipDelegate has closed because the IMS service does not support the creation of
-     * SipDelegates.
-     * @hide
-     */
-    public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3;
-
-    /**
      * The SipDelegate has been closed due to the user disabling RCS.
      * @hide
      */
-    public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4;
+    public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3;
+
+    /**
+     * The SipDelegate has been closed due to the subscription associated with this delegate being
+     * torn down.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -254,8 +254,8 @@
             SIP_DELEGATE_DESTROY_REASON_UNKNOWN,
             SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD,
             SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
-            SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED,
-            SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS
+            SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS,
+            SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN
     })
     public @interface SipDelegateDestroyReason {}
 
@@ -316,6 +316,9 @@
      * always be available to handle incoming messages. One mechanism that can be used for this is
      * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps
      * a persistent binding to when the app is the default SMS application.
+     * <p>
+     * Note: the ability to create SipDelegates is only available applications running as the
+     * primary user.
      * @param request The parameters that are associated with the SipDelegate creation request that
      *                will be used to create the SipDelegate connection.
      * @param executor The executor that will be used to call the callbacks associated with this
@@ -346,8 +349,8 @@
                 throw new ImsException("Telephony server is down",
                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
             }
-            controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(),
-                    wrapper.getMessageCallbackBinder());
+            controller.createSipDelegate(mSubId, request, mContext.getOpPackageName(),
+                    wrapper.getStateCallbackBinder(), wrapper.getMessageCallbackBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index f218e35..c6d9a86 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -63,7 +63,7 @@
 
     // SipDelegateManager
     boolean isSipDelegateSupported(int subId);
-    void createSipDelegate(int subId, in DelegateRequest request,
+    void createSipDelegate(int subId, in DelegateRequest request, String packageName,
             ISipDelegateConnectionStateCallback delegateState,
             ISipDelegateMessageCallback delegateMessage);
     void destroySipDelegate(int subId, ISipDelegate connection, int reason);
diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
index cd88839..3438587 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -26,7 +26,7 @@
  * {@hide}
  */
 oneway interface ISipTransport {
-    void createSipDelegate(in DelegateRequest request, ISipDelegateStateCallback dc,
+    void createSipDelegate(int subId, in DelegateRequest request, ISipDelegateStateCallback dc,
             ISipDelegateMessageCallback mc);
     void destroySipDelegate(ISipDelegate delegate, int reason);
 }
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index b48f631..93d438c 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -58,11 +58,11 @@
 
     private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() {
         @Override
-        public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc,
-                ISipDelegateMessageCallback mc) {
+        public void createSipDelegate(int subId, DelegateRequest request,
+                ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc) {
             final long token = Binder.clearCallingIdentity();
             try {
-                mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc));
+                mBinderExecutor.execute(() -> createSipDelegateInternal(subId, request, dc, mc));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -105,6 +105,7 @@
      * This method will be called on the Executor specified in
      * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
      *
+     * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}.
      * @param request A SIP delegate request containing the parameters that the remote RCS
      * application wishes to use.
      * @param dc A callback back to the remote application to be used to communicate state callbacks
@@ -113,9 +114,9 @@
      *           remote application and acknowledge the sending of outgoing SIP messages.
      * @hide
      */
-    public void createSipDelegate(@NonNull DelegateRequest request,
+    public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
             @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
-        throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+        throw new UnsupportedOperationException("createSipDelegate not implemented!");
     }
 
     /**
@@ -136,11 +137,11 @@
         throw new UnsupportedOperationException("destroySipDelegate not implemented!");
     }
 
-    private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb,
-            ISipDelegateMessageCallback mc) {
+    private void createSipDelegateInternal(int subId, DelegateRequest r,
+            ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) {
         SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
         mDelegates.add(wrapper);
-        createSipDelegate(r, wrapper, wrapper);
+        createSipDelegate(subId, r, wrapper, wrapper);
     }
 
     private void destroySipDelegateInternal(ISipDelegate d, int reason) {
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 76fc4f7..6fbde50 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -113,6 +113,7 @@
     public static final int EVENT_NR_TIMER_WATCHDOG = BASE + 53;
     public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54;
     public static final int EVENT_SIM_STATE_UPDATED = BASE + 55;
+    public static final int EVENT_APN_UNTHROTTLED = BASE + 56;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0bd4851..b524549 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -36,6 +36,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
+import android.telephony.ThermalMitigationRequest;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.ICellInfoCallback;
 import android.telephony.ModemActivityInfo;
@@ -2154,6 +2155,8 @@
     oneway void setSystemSelectionChannels(in List<RadioAccessSpecifier> specifiers,
             int subId, IBooleanConsumer resultCallback);
 
+    List<RadioAccessSpecifier> getSystemSelectionChannels(int subId);
+
     boolean isMvnoMatched(int subId, int mvnoType, String mvnoMatchData);
 
     /**
@@ -2247,4 +2250,22 @@
      * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
      */
     CarrierBandwidth getCarrierBandwidth(int subId);
+
+    /**
+     * Checks whether the device supports the given capability on the radio interface.
+     *
+     * @param capability the name of the capability
+     * @return the availability of the capability
+     */
+    boolean isRadioInterfaceCapabilitySupported(String capability);
+
+    /**
+     * Thermal mitigation request to control functionalities at modem.
+     *
+     * @param subId the id of the subscription
+     * @param thermalMitigationRequest holds the parameters necessary for the request.
+     * @throws InvalidThermalMitigationRequestException if the parametes are invalid.
+     */
+    int sendThermalMitigationRequest(int subId,
+            in ThermalMitigationRequest thermalMitigationRequest);
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9d4072f..42dee0e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -519,6 +519,9 @@
     int RIL_REQUEST_RELEASE_PDU_SESSION_ID = 216;
     int RIL_REQUEST_START_HANDOVER = 217;
     int RIL_REQUEST_CANCEL_HANDOVER = 218;
+    int RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS = 219;
+    int RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES = 220;
+    int RIL_REQUEST_SET_DATA_THROTTLING = 221;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
new file mode 100644
index 0000000..58ccec7
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -0,0 +1,25 @@
+// 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 {
+    name: "BatteryStatsPerfTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "apct-perftests-utils",
+        "truth-prebuilt",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml
new file mode 100644
index 0000000..7633d52
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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="com.android.frameworks.perftests.batterystats">
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BATTERY_STATS"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.frameworks.perftests.batterystats"/>
+</manifest>
diff --git a/tests/BatteryStatsPerfTest/AndroidTest.xml b/tests/BatteryStatsPerfTest/AndroidTest.xml
new file mode 100644
index 0000000..2f9e114
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<configuration description="Runs BatteryStats service Performance Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="BatteryStatsPerfTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="BatteryStatsPerfTests"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.perftests.batterystats"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
new file mode 100644
index 0000000..6266cda
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+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.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BatteryStatsHelperPerfTest {
+
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Measures the performance of {@link BatteryStatsHelper#getStats()}, which triggers
+     * a battery stats sync on every iteration.
+     */
+    @Test
+    public void testGetStats_forceUpdate() {
+        final Context context = InstrumentationRegistry.getContext();
+        final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
+                true /* collectBatteryBroadcast */);
+        statsHelper.create((Bundle) null);
+        statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            statsHelper.clearStats();
+            state.resumeTiming();
+
+            statsHelper.getStats();
+
+            assertThat(statsHelper.getUsageList()).isNotEmpty();
+        }
+    }
+
+    /**
+     * Measures performance of the {@link BatteryStatsHelper#getStats(boolean)}, which does
+     * not trigger a sync and just returns current values.
+     */
+    @Test
+    public void testGetStats_cached() {
+        final Context context = InstrumentationRegistry.getContext();
+        final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
+                true /* collectBatteryBroadcast */);
+        statsHelper.create((Bundle) null);
+        statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            statsHelper.clearStats();
+            state.resumeTiming();
+
+            statsHelper.getStats(false /* forceUpdate */);
+
+            assertThat(statsHelper.getUsageList()).isNotEmpty();
+        }
+    }
+
+    @Test
+    public void testPowerCalculation() {
+        final Context context = InstrumentationRegistry.getContext();
+        final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
+                true /* collectBatteryBroadcast */);
+        statsHelper.create((Bundle) null);
+        statsHelper.getStats();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // This will use the cached BatteryStatsObject
+            statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
+
+            assertThat(statsHelper.getUsageList()).isNotEmpty();
+        }
+    }
+
+    @Test
+    public void testEndToEnd() {
+        final Context context = InstrumentationRegistry.getContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
+                    true /* collectBatteryBroadcast */);
+            statsHelper.create((Bundle) null);
+            statsHelper.clearStats();
+            statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
+
+            state.pauseTiming();
+
+            List<BatterySipper> usageList = statsHelper.getUsageList();
+            double power = 0;
+            for (int i = 0; i < usageList.size(); i++) {
+                BatterySipper sipper = usageList.get(i);
+                power += sipper.sumPower();
+            }
+
+            assertThat(power).isGreaterThan(0.0);
+
+            state.resumeTiming();
+        }
+    }
+}
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index 98e02ce..58f56f2 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -27,6 +27,8 @@
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <!-- Enable / Disable tracing !-->
     <uses-permission android:name="android.permission.DUMP" />
+    <!-- Force-stop test apps -->
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
     <!-- Run layers trace -->
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
     <!-- Workaround grant runtime permission exception from b/152733071 -->
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 647da2a..48031de 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -33,6 +33,8 @@
         "kotlinx-coroutines-android",
         "flickerlib",
         "truth-prebuilt",
+        "cts-wm-util",
+        "CtsSurfaceValidatorLib",
     ],
 }
 
@@ -43,6 +45,7 @@
     ],
     shared_libs: [
         "libutils",
+        "libui",
         "libgui",
         "liblog",
         "libandroid",
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
index 95885c1..c910ecd 100644
--- a/tests/SurfaceViewBufferTests/AndroidManifest.xml
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -23,12 +23,19 @@
     <uses-permission android:name="android.permission.DUMP" />
     <!-- Enable / Disable sv blast adapter !-->
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- Readback virtual display output !-->
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <!-- Save failed test bitmap images !-->
+    <uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/>
 
     <application android:allowBackup="false"
          android:supportsRtl="true">
         <activity android:name=".MainActivity"
                   android:taskAffinity="com.android.test.MainActivity"
                   android:theme="@style/AppTheme"
+                  android:configChanges="orientation|screenSize"
                   android:label="SurfaceViewBufferTestApp"
                   android:exported="true">
             <intent-filter>
@@ -36,6 +43,10 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
+                 android:foregroundServiceType="mediaProjection"
+                 android:enabled="true">
+        </service>
         <uses-library android:name="android.test.runner"/>
     </application>
 
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
index 0c86524..ce226fd 100644
--- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -32,6 +32,7 @@
 extern "C" {
 int i = 0;
 static ANativeWindow* sAnw;
+static std::map<uint32_t /* slot */, ANativeWindowBuffer*> sBuffers;
 
 JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
                                                                      jobject surfaceObject) {
@@ -39,11 +40,14 @@
     assert(sAnw);
     android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
     surface->enableFrameTimestamps(true);
+    surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false);
+    native_window_set_usage(sAnw, GRALLOC_USAGE_SW_WRITE_OFTEN);
+    native_window_set_buffers_format(sAnw, HAL_PIXEL_FORMAT_RGBA_8888);
     return 0;
 }
 
 JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
-        JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
+        JNIEnv*, jclass, jlong jFrameNumber, jint timeoutMs) {
     using namespace std::chrono_literals;
     assert(sAnw);
     android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
@@ -63,8 +67,8 @@
                                     &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime);
         if (outDisplayPresentTime < 0) {
             auto end = std::chrono::steady_clock::now();
-            if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() >
-                timeoutSec) {
+            if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() >
+                timeoutMs) {
                 return -1;
             }
         }
@@ -99,4 +103,121 @@
     assert(sAnw);
     return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
 }
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersTransform(
+        JNIEnv* /* env */, jclass /* clazz */, jint transform) {
+    assert(sAnw);
+    return native_window_set_buffers_transform(sAnw, transform);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetScalingMode(JNIEnv* /* env */,
+                                                                                jclass /* clazz */,
+                                                                                jint scalingMode) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    return surface->setScalingMode(scalingMode);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(JNIEnv* /* env */,
+                                                                               jclass /* clazz */,
+                                                                               jint slot,
+                                                                               jint timeoutMs) {
+    assert(sAnw);
+    ANativeWindowBuffer* anb;
+    int fenceFd;
+    int result = sAnw->dequeueBuffer(sAnw, &anb, &fenceFd);
+    if (result != android::OK) {
+        return result;
+    }
+    sBuffers[slot] = anb;
+    android::sp<android::Fence> fence(new android::Fence(fenceFd));
+    int waitResult = fence->wait(timeoutMs);
+    if (waitResult != android::OK) {
+        sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
+        sBuffers[slot] = nullptr;
+        return waitResult;
+    }
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceCancelBuffer(JNIEnv* /* env */,
+                                                                              jclass /* clazz */,
+                                                                              jint slot) {
+    assert(sAnw);
+    assert(sBuffers[slot]);
+    int result = sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
+    sBuffers[slot] = nullptr;
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_drawBuffer(JNIEnv* env,
+                                                                     jclass /* clazz */, jint slot,
+                                                                     jintArray jintArrayColor) {
+    assert(sAnw);
+    assert(sBuffers[slot]);
+
+    int* color = env->GetIntArrayElements(jintArrayColor, nullptr);
+
+    ANativeWindowBuffer* buffer = sBuffers[slot];
+    android::sp<android::GraphicBuffer> graphicBuffer(static_cast<android::GraphicBuffer*>(buffer));
+    const android::Rect bounds(buffer->width, buffer->height);
+    android::Region newDirtyRegion;
+    newDirtyRegion.set(bounds);
+
+    void* vaddr;
+    int fenceFd = -1;
+    graphicBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                             newDirtyRegion.bounds(), &vaddr, fenceFd);
+
+    for (int32_t row = 0; row < buffer->height; row++) {
+        uint8_t* dst = static_cast<uint8_t*>(vaddr) + (buffer->stride * row) * 4;
+        for (int32_t column = 0; column < buffer->width; column++) {
+            dst[0] = color[0];
+            dst[1] = color[1];
+            dst[2] = color[2];
+            dst[3] = color[3];
+            dst += 4;
+        }
+    }
+    graphicBuffer->unlockAsync(&fenceFd);
+    env->ReleaseIntArrayElements(jintArrayColor, color, JNI_ABORT);
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNIEnv* /* env */,
+                                                                             jclass /* clazz */,
+                                                                             jint slot,
+                                                                             jboolean freeSlot) {
+    assert(sAnw);
+    assert(sBuffers[slot]);
+    int result = sAnw->queueBuffer(sAnw, sBuffers[slot], -1);
+    if (freeSlot) {
+        sBuffers[slot] = nullptr;
+    }
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
+        JNIEnv* /* env */, jclass /* clazz */, jint count) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    int result = native_window_set_buffer_count(sAnw, count);
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetSharedBufferMode(
+        JNIEnv* /* env */, jclass /* clazz */, jboolean shared) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    int result = native_window_set_shared_buffer_mode(sAnw, shared);
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetAutoRefresh(
+        JNIEnv* /* env */, jclass /* clazz */, jboolean autoRefresh) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    int result = native_window_set_auto_refresh(sAnw, autoRefresh);
+    return result;
+}
 }
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
new file mode 100644
index 0000000..eb16bad
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -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.test
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+    /** Submit buffers as fast as possible and make sure they are presented on display */
+    @Test
+    fun testQueueBuffers() {
+        val numFrames = 100L
+        val trace = withTrace {
+            for (i in 1..numFrames) {
+                it.mSurfaceProxy.ANativeWindowLock()
+                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+            }
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 1000 /* ms */))
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+    }
+
+    @Test
+    fun testSetBufferScalingMode_outOfOrderQueueBuffer() {
+        val trace = withTrace {
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+
+            it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(2, 5000 /* ms */))
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", 1..2L)
+    }
+
+    @Test
+    fun testSetBufferScalingMode_multipleDequeueBuffer() {
+        val numFrames = 20L
+        val trace = withTrace {
+            for (count in 1..(numFrames / 2)) {
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+
+                it.mSurfaceProxy.SurfaceQueueBuffer(0)
+                it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            }
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+    }
+
+    @Test
+    fun testSetBufferCount_queueMaxBufferCountMinusOne() {
+        val numBufferCount = 8
+        val numFrames = numBufferCount * 5L
+        val trace = withTrace {
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetBufferCount(numBufferCount + 1))
+            for (i in 1..numFrames / numBufferCount) {
+                for (bufferSlot in 0..numBufferCount - 1) {
+                    assertEquals(0,
+                            it.mSurfaceProxy.SurfaceDequeueBuffer(bufferSlot, 1000 /* ms */))
+                }
+
+                for (bufferSlot in 0..numBufferCount - 1) {
+                    it.mSurfaceProxy.SurfaceQueueBuffer(bufferSlot)
+                }
+            }
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
new file mode 100644
index 0000000..95a7fd5
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+    @Test
+    fun testSetBuffersGeometry_0x0_rejectsBuffer() {
+        val trace = withTrace {
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+                    R8G8B8A8_UNORM)
+            it.mSurfaceProxy.ANativeWindowLock()
+            it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+            it.mSurfaceProxy.ANativeWindowLock()
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
+            // Submit buffer one with a different size which should be rejected
+            it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+
+            // submit a buffer with the default buffer size
+            it.mSurfaceProxy.ANativeWindowLock()
+            it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+            it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */)
+        }
+        // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
+        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+
+        // Verify the next buffer is submitted with the correct size
+        assertThat(trace).layer("SurfaceView", 3).also {
+            it.hasBufferSize(defaultBufferSize)
+            // scaling mode is not passed down to the layer for blast
+            if (useBlastAdapter) {
+                it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+            } else {
+                it.hasScalingMode(ScalingMode.FREEZE.ordinal)
+            }
+        }
+    }
+
+    @Test
+    fun testSetBufferScalingMode_freeze() {
+        val bufferSize = Point(300, 200)
+        val trace = withTrace {
+            it.drawFrame()
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+                    R8G8B8A8_UNORM)
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+            // Change buffer size and set scaling mode to freeze
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+                    R8G8B8A8_UNORM)
+
+            // first dequeued buffer does not have the new size so it should be rejected.
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+            it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+        }
+
+        // verify buffer size is reset to default buffer size
+        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+        assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+    }
+
+    @Test
+    fun testSetBufferScalingMode_freeze_withBufferRotation() {
+        val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
+        val trace = withTrace {
+            it.drawFrame()
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
+                    R8G8B8A8_UNORM)
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+            // Change buffer size and set scaling mode to freeze
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+                    R8G8B8A8_UNORM)
+
+            // first dequeued buffer does not have the new size so it should be rejected.
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            // add a buffer transform so the buffer size is correct.
+            it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90)
+            it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+        }
+
+        // verify buffer size is reset to default buffer size
+        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+        assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+        assertThat(trace).layer("SurfaceView", 3).hasBufferOrientation(Transform.ROT_90.value)
+    }
+
+    @Test
+    fun testRejectedBuffersAreReleased() {
+        val bufferSize = Point(300, 200)
+        val trace = withTrace {
+            for (count in 0 until 5) {
+                it.drawFrame()
+                assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 1L,
+                        500 /* ms */), 0)
+                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+                        R8G8B8A8_UNORM)
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+                // Change buffer size and set scaling mode to freeze
+                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+                        R8G8B8A8_UNORM)
+
+                // first dequeued buffer does not have the new size so it should be rejected.
+                it.mSurfaceProxy.SurfaceQueueBuffer(0)
+                it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+                it.mSurfaceProxy.SurfaceQueueBuffer(1)
+                assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 3L,
+                        500 /* ms */), 0)
+            }
+        }
+
+        for (count in 0 until 5) {
+            assertThat(trace).layer("SurfaceView", (count * 3) + 1L)
+                    .hasBufferSize(defaultBufferSize)
+            assertThat(trace).layer("SurfaceView", (count * 3) + 2L)
+                    .doesNotExist()
+            assertThat(trace).layer("SurfaceView", (count * 3) + 3L)
+                    .hasBufferSize(bufferSize)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
new file mode 100644
index 0000000..03f8c05
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+    @Test
+    fun testSetBuffersGeometry_0x0_resetsBufferSize() {
+        val trace = withTrace {
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
+                    R8G8B8A8_UNORM)
+            it.mSurfaceProxy.ANativeWindowLock()
+            it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+            it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+        }
+
+        // verify buffer size is reset to default buffer size
+        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+    }
+
+    @Test
+    fun testSetBuffersGeometry_smallerThanBuffer() {
+        val bufferSize = Point(300, 200)
+        val trace = withTrace {
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+                    R8G8B8A8_UNORM)
+            it.drawFrame()
+            it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+        }
+
+        assertThat(trace).layer("SurfaceView", 1).also {
+            it.hasBufferSize(bufferSize)
+            it.hasLayerSize(defaultBufferSize)
+            it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+        }
+    }
+
+    @Test
+    fun testSetBuffersGeometry_largerThanBuffer() {
+        val bufferSize = Point(3000, 2000)
+        val trace = withTrace {
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+                    R8G8B8A8_UNORM)
+            it.drawFrame()
+            it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+        }
+
+        assertThat(trace).layer("SurfaceView", 1).also {
+            it.hasBufferSize(bufferSize)
+            it.hasLayerSize(defaultBufferSize)
+            it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+        }
+    }
+
+    @Test
+    fun testSetBufferScalingMode_freeze() {
+        val bufferSize = Point(300, 200)
+        val trace = withTrace {
+            it.drawFrame()
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+                    R8G8B8A8_UNORM)
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+            // Change buffer size and set scaling mode to freeze
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+                    R8G8B8A8_UNORM)
+
+            // first dequeued buffer does not have the new size so it should be rejected.
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+            it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+        }
+
+        // verify buffer size is reset to default buffer size
+        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+        assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+    }
+
+    @Test
+    fun testSetBuffersTransform_FLIP() {
+        val transforms = arrayOf(Transform.FLIP_H, Transform.FLIP_V, Transform.ROT_180).withIndex()
+        for ((index, transform) in transforms) {
+            val trace = withTrace {
+                it.mSurfaceProxy.ANativeWindowSetBuffersTransform(transform)
+                it.mSurfaceProxy.ANativeWindowLock()
+                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+                it.mSurfaceProxy.waitUntilBufferDisplayed(index + 1L, 500 /* ms */)
+            }
+
+            assertThat(trace).layer("SurfaceView", index + 1L).also {
+                it.hasBufferSize(defaultBufferSize)
+                it.hasLayerSize(defaultBufferSize)
+                it.hasBufferOrientation(transform.value)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
new file mode 100644
index 0000000..eac3041
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
+        SurfaceTracingTestBase(useBlastAdapter) {
+    @Before
+    override fun setup() {
+        scenarioRule.getScenario().onActivity {
+            it.rotate90()
+        }
+        instrumentation.waitForIdleSync()
+        super.setup()
+    }
+
+    @Test
+    fun testSetBufferScalingMode_freeze_withInvDisplayTransform() {
+        assumeFalse("Blast does not support buffer rejection with Inv display " +
+                "transform since the only user for this hidden api is camera which does not use" +
+                "fixed scaling mode.", useBlastAdapter)
+
+        val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
+        val trace = withTrace {
+            // Inverse display transforms are sticky AND they are only consumed by the sf after
+            // a valid buffer has been acquired.
+            it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.INVERSE_DISPLAY.value)
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
+                    R8G8B8A8_UNORM)
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+            // Change buffer size and set scaling mode to freeze
+            it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+                    R8G8B8A8_UNORM)
+
+            // first dequeued buffer does not have the new size so it should be rejected.
+            it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90.value)
+            it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            it.mSurfaceProxy.ANativeWindowSetBuffersTransform(0)
+            it.mSurfaceProxy.SurfaceQueueBuffer(1)
+            assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+        }
+
+        // verify buffer size is reset to default buffer size
+        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+        assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
index b1e1336..ed79054 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
@@ -15,51 +15,80 @@
  */
 package com.android.test
 
-import android.app.Activity
 import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
 import android.graphics.Color
 import android.graphics.Paint
+import android.graphics.Point
 import android.graphics.Rect
 import android.os.Bundle
 import android.view.Gravity
 import android.view.Surface
 import android.view.SurfaceHolder
 import android.view.SurfaceView
+import android.view.View
+import android.view.WindowManager
+import android.view.cts.surfacevalidator.CapturedActivity
 import android.widget.FrameLayout
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
-class MainActivity : Activity() {
+class MainActivity : CapturedActivity() {
     val mSurfaceProxy = SurfaceProxy()
     private var mSurfaceHolder: SurfaceHolder? = null
     private val mDrawLock = ReentrantLock()
+    var mSurfaceView: SurfaceView? = null
 
     val surface: Surface? get() = mSurfaceHolder?.surface
 
-    public override fun onCreate(savedInstanceState: Bundle?) {
+    override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        addSurfaceView(Rect(0, 0, 500, 200))
+        addSurfaceView(Point(500, 200))
+        window.decorView.apply {
+            systemUiVisibility =
+                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
+        }
     }
 
-    fun addSurfaceView(size: Rect): CountDownLatch {
+    override fun getCaptureDurationMs(): Long {
+        return 30000
+    }
+
+    fun addSurfaceView(size: Point): CountDownLatch {
         val layout = findViewById<FrameLayout>(android.R.id.content)
         val surfaceReadyLatch = CountDownLatch(1)
-        val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
-        layout.addView(surfaceView,
-                FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT)
+        mSurfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
+        layout.addView(mSurfaceView!!,
+                FrameLayout.LayoutParams(size.x, size.y, Gravity.TOP or Gravity.LEFT)
                         .also { it.setMargins(100, 100, 0, 0) })
+
         return surfaceReadyLatch
     }
 
+    fun enableSeamlessRotation() {
+        val p: WindowManager.LayoutParams = window.attributes
+        p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+        window.attributes = p
+    }
+
+    fun rotate90() {
+        if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+        } else {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+        }
+    }
+
     private fun createSurfaceView(
         context: Context,
-        size: Rect,
+        size: Point,
         surfaceReadyLatch: CountDownLatch
     ): SurfaceView {
         val surfaceView = SurfaceView(context)
         surfaceView.setWillNotDraw(false)
-        surfaceView.holder.setFixedSize(size.width(), size.height())
+        surfaceView.holder.setFixedSize(size.x, size.y)
         surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
             override fun surfaceCreated(holder: SurfaceHolder) {
                 mDrawLock.withLock {
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
new file mode 100644
index 0000000..df3d30e
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.test
+
+import android.annotation.ColorInt
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.server.wm.WindowManagerState.getLogicalDisplaySize
+import android.view.cts.surfacevalidator.CapturedActivity
+import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase
+import android.view.cts.surfacevalidator.PixelChecker
+import android.view.cts.surfacevalidator.RectChecker
+import android.widget.FrameLayout
+import androidx.test.rule.ActivityTestRule
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import java.util.concurrent.CountDownLatch
+
+open class ScreenRecordTestBase(useBlastAdapter: Boolean) :
+        SurfaceViewBufferTestBase(useBlastAdapter) {
+    @get:Rule
+    var mActivityRule = ActivityTestRule(MainActivity::class.java)
+
+    private lateinit var mActivity: MainActivity
+
+    @Before
+    override fun setup() {
+        super.setup()
+        mActivity = mActivityRule.launchActivity(Intent())
+        lateinit var surfaceReadyLatch: CountDownLatch
+        runOnUiThread {
+            it.dismissPermissionDialog()
+            it.setLogicalDisplaySize(getLogicalDisplaySize())
+            surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+        }
+        surfaceReadyLatch.await()
+        // sleep to finish animations
+        instrumentation.waitForIdleSync()
+    }
+
+    @After
+    override fun teardown() {
+        super.teardown()
+        mActivityRule.finishActivity()
+    }
+
+    fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
+        mActivityRule.runOnUiThread {
+            predicate(mActivity)
+        }
+    }
+
+    fun withScreenRecording(
+        boundsToCheck: Rect,
+        @ColorInt color: Int,
+        predicate: (it: MainActivity) -> Unit
+    ): CapturedActivity.TestResult {
+        val testCase = object : ISurfaceValidatorTestCase {
+            override fun getChecker(): PixelChecker = RectChecker(boundsToCheck, color)
+            override fun start(context: Context, parent: FrameLayout) {
+                predicate(mActivity)
+            }
+            override fun end() { /* do nothing */ }
+        }
+
+        return mActivity.runTest(testCase)
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
new file mode 100644
index 0000000..996a1d3
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.test
+
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.SystemClock
+import android.view.cts.surfacevalidator.PixelColor
+import junit.framework.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class SharedBufferModeScreenRecordTests(useBlastAdapter: Boolean) :
+        ScreenRecordTestBase(useBlastAdapter) {
+
+    /** When auto refresh is set, surface flinger will wake up and refresh the display presenting
+     * the latest content in the buffer.
+     */
+    @Test
+    fun testAutoRefresh() {
+        var svBounds = Rect()
+        runOnUiThread {
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+            it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
+            assertEquals(0,
+                    it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
+
+            svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
+            val position = Rect()
+            it.mSurfaceView!!.getBoundsOnScreen(position)
+            svBounds.offsetTo(position.left, position.top)
+
+            // wait for buffers from other layers to be latched and transactions to be processed before
+            // updating the buffer
+            SystemClock.sleep(4000)
+        }
+
+        val result = withScreenRecording(svBounds, PixelColor.RED) {
+            it.mSurfaceProxy.drawBuffer(0, Color.RED)
+        }
+        val failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames)
+
+        assertTrue("Error: " + result.failFrames +
+                " incorrect frames observed (out of " + (result.failFrames + result.passFrames) +
+                " frames)", failRatio < 0.05)
+        assertTrue("Error: Did not receive sufficient frame updates expected: >1000 actual:" +
+                result.passFrames, result.passFrames > 1000)
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
new file mode 100644
index 0000000..ae662506
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -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.test
+
+import android.graphics.Color
+import android.graphics.Rect
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+    /** Sanity test to check each buffer is presented if its submitted with enough delay
+     * for SF to present the buffers. */
+    @Test
+    fun testCanPresentBuffers() {
+        val numFrames = 15L
+        val trace = withTrace {
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+            for (i in 1..numFrames) {
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+                it.mSurfaceProxy.SurfaceQueueBuffer(0)
+                assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(i, 5000 /* ms */))
+            }
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+    }
+
+    /** Submit buffers as fast as possible testing that we are not blocked when dequeuing the buffer
+     * by setting the dequeue timeout to 1ms and checking that we present the newest buffer. */
+    @Test
+    fun testFastQueueBuffers() {
+        val numFrames = 15L
+        val trace = withTrace {
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+            for (i in 1..numFrames) {
+                assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+                it.mSurfaceProxy.SurfaceQueueBuffer(0)
+            }
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+        }
+
+        assertThat(trace).hasFrameSequence("SurfaceView", numFrames..numFrames)
+    }
+
+    /** Keep overwriting the buffer without queuing buffers and check that we present the latest
+     * buffer content. */
+    @Test
+    fun testAutoRefresh() {
+        var svBounds = Rect()
+        runOnUiThread {
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+            assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
+            assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+            it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
+            assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
+
+            svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
+            val position = Rect()
+            it.mSurfaceView!!.getBoundsOnScreen(position)
+            svBounds.offsetTo(position.left, position.top)
+        }
+
+        runOnUiThread {
+            it.mSurfaceProxy.drawBuffer(0, Color.RED)
+            checkPixels(svBounds, Color.RED)
+            it.mSurfaceProxy.drawBuffer(0, Color.GREEN)
+            checkPixels(svBounds, Color.GREEN)
+            it.mSurfaceProxy.drawBuffer(0, Color.BLUE)
+            checkPixels(svBounds, Color.BLUE)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
index 884aae41..cfbd3ac 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -16,17 +16,46 @@
 
 package com.android.test
 
+import android.annotation.ColorInt
+import android.graphics.Color
+import android.graphics.Point
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+
 class SurfaceProxy {
     init {
         System.loadLibrary("surface_jni")
     }
 
     external fun setSurface(surface: Any)
-    external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
+    external fun waitUntilBufferDisplayed(frameNumber: Long, timeoutMs: Int): Int
     external fun draw()
+    fun drawBuffer(slot: Int, @ColorInt c: Int) {
+        drawBuffer(slot, intArrayOf(Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)))
+    }
+    external fun drawBuffer(slot: Int, color: IntArray)
 
     // android/native_window.h functions
     external fun ANativeWindowLock()
     external fun ANativeWindowUnlockAndPost()
+    fun ANativeWindowSetBuffersGeometry(surface: Any, size: Point, format: Int) {
+        ANativeWindowSetBuffersGeometry(surface, size.x, size.y, format)
+    }
     external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
+    fun ANativeWindowSetBuffersTransform(transform: Transform) {
+        ANativeWindowSetBuffersTransform(transform.value)
+    }
+    external fun ANativeWindowSetBuffersTransform(transform: Int)
+
+    // gui/Surface.h functions
+    fun SurfaceSetScalingMode(scalingMode: ScalingMode) {
+        SurfaceSetScalingMode(scalingMode.ordinal)
+    }
+    external fun SurfaceSetScalingMode(scalingMode: Int)
+    external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
+    external fun SurfaceCancelBuffer(slot: Int)
+    external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
+    external fun NativeWindowSetBufferCount(count: Int): Int
+    external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
+    external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
 }
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
new file mode 100644
index 0000000..cd4b385
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.test
+
+import android.annotation.ColorInt
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.withSFTracing
+import com.android.server.wm.flicker.traces.layers.LayersTrace
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import java.io.FileOutputStream
+import java.io.IOException
+import java.util.concurrent.CountDownLatch
+
+open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
+        SurfaceViewBufferTestBase(useBlastAdapter) {
+    @get:Rule
+    var scenarioRule: ActivityScenarioRule<MainActivity> =
+            ActivityScenarioRule<MainActivity>(MainActivity::class.java)
+
+    @Before
+    override fun setup() {
+        super.setup()
+        stopLayerTrace()
+        addSurfaceView()
+    }
+
+    @After
+    override fun teardown() {
+        super.teardown()
+        scenarioRule.getScenario().close()
+    }
+
+    fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
+        return withSFTracing(instrumentation, TRACE_FLAGS) {
+            scenarioRule.getScenario().onActivity {
+                predicate(it)
+            }
+        }
+    }
+
+    fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
+        scenarioRule.getScenario().onActivity {
+            predicate(it)
+        }
+    }
+
+    private fun addSurfaceView() {
+        lateinit var surfaceReadyLatch: CountDownLatch
+        scenarioRule.getScenario().onActivity {
+            surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+        }
+        surfaceReadyLatch.await()
+        // sleep to finish animations
+        instrumentation.waitForIdleSync()
+    }
+
+    private fun stopLayerTrace() {
+        val tmpDir = instrumentation.targetContext.dataDir.toPath()
+        LayersTraceMonitor(tmpDir).stop()
+    }
+
+    fun checkPixels(bounds: Rect, @ColorInt color: Int) {
+        val screenshot = instrumentation.getUiAutomation().takeScreenshot()
+        val pixels = IntArray(screenshot.width * screenshot.height)
+        screenshot.getPixels(pixels, 0, screenshot.width, 0, 0, screenshot.width, screenshot.height)
+        for (i in bounds.left + 10..bounds.right - 10) {
+            for (j in bounds.top + 10..bounds.bottom - 10) {
+                val actualColor = pixels[j * screenshot.width + i]
+                if (actualColor != color) {
+                    val screenshotPath = instrumentation.targetContext
+                            .getExternalFilesDir(null)?.resolve("screenshot.png")
+                    try {
+                        FileOutputStream(screenshotPath).use { out ->
+                            screenshot.compress(Bitmap.CompressFormat.PNG, 100, out)
+                        }
+                        Log.e("SurfaceViewBufferTests", "Bitmap written to $screenshotPath")
+                    } catch (e: IOException) {
+                        Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e)
+                    }
+                }
+                Assert.assertEquals("Checking $bounds found mismatch $i,$j",
+                        Color.valueOf(color), Color.valueOf(actualColor))
+            }
+        }
+    }
+
+    private companion object {
+        private const val TRACE_FLAGS =
+                (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
+    }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
deleted file mode 100644
index b48a91d..0000000
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
+++ /dev/null
@@ -1,185 +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.test
-
-import android.app.Instrumentation
-import android.graphics.Rect
-import android.provider.Settings
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import java.util.concurrent.CountDownLatch
-import kotlin.properties.Delegates
-
-@RunWith(Parameterized::class)
-class SurfaceViewBufferTest(val useBlastAdapter: Boolean) {
-    private var mInitialUseBlastConfig by Delegates.notNull<Int>()
-
-    @get:Rule
-    var scenarioRule: ActivityScenarioRule<MainActivity> =
-            ActivityScenarioRule<MainActivity>(MainActivity::class.java)
-
-    protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    val defaultBufferSize = Rect(0, 0, 640, 480)
-
-    @Before
-    fun setup() {
-        mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver,
-                "use_blast_adapter_sv", 0)
-        val enable = if (useBlastAdapter) 1 else 0
-        Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
-                enable)
-        val tmpDir = instrumentation.targetContext.dataDir.toPath()
-        LayersTraceMonitor(tmpDir).stop()
-
-        lateinit var surfaceReadyLatch: CountDownLatch
-        scenarioRule.getScenario().onActivity {
-            surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
-        }
-        surfaceReadyLatch.await()
-    }
-
-    @After
-    fun teardown() {
-        scenarioRule.getScenario().close()
-        Settings.Global.putInt(instrumentation.context.contentResolver,
-                "use_blast_adapter_sv", mInitialUseBlastConfig)
-    }
-
-    @Test
-    fun testSetBuffersGeometry_0x0_resetsBufferSize() {
-        val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
-            scenarioRule.getScenario().onActivity {
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
-                        R8G8B8A8_UNORM)
-                it.mSurfaceProxy.ANativeWindowLock()
-                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-                it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
-            }
-        }
-
-        // verify buffer size is reset to default buffer size
-        assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
-    }
-
-    @Test
-    fun testSetBuffersGeometry_0x0_rejectsBuffer() {
-        val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
-            scenarioRule.getScenario().onActivity {
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
-                        R8G8B8A8_UNORM)
-                it.mSurfaceProxy.ANativeWindowLock()
-                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-                it.mSurfaceProxy.ANativeWindowLock()
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
-                // Submit buffer one with a different size which should be rejected
-                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-
-                // submit a buffer with the default buffer size
-                it.mSurfaceProxy.ANativeWindowLock()
-                it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-                it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */)
-            }
-        }
-        // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
-        assertThat(trace).layer("SurfaceView", 2).doesNotExist()
-
-        // Verify the next buffer is submitted with the correct size
-        assertThat(trace).layer("SurfaceView", 3).also {
-            it.hasBufferSize(defaultBufferSize)
-            it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */)
-        }
-    }
-
-    @Test
-    fun testSetBuffersGeometry_smallerThanBuffer() {
-        val bufferSize = Rect(0, 0, 300, 200)
-        val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
-            scenarioRule.getScenario().onActivity {
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
-                        bufferSize.height(), R8G8B8A8_UNORM)
-                it.drawFrame()
-                it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
-            }
-        }
-
-        assertThat(trace).layer("SurfaceView", 1).also {
-            it.hasBufferSize(bufferSize)
-            it.hasLayerSize(defaultBufferSize)
-            it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
-        }
-    }
-
-    @Test
-    fun testSetBuffersGeometry_largerThanBuffer() {
-        val bufferSize = Rect(0, 0, 3000, 2000)
-        val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
-            scenarioRule.getScenario().onActivity {
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
-                        bufferSize.height(), R8G8B8A8_UNORM)
-                it.drawFrame()
-                it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
-            }
-        }
-
-        assertThat(trace).layer("SurfaceView", 1).also {
-            it.hasBufferSize(bufferSize)
-            it.hasLayerSize(defaultBufferSize)
-            it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
-        }
-    }
-
-    /** Submit buffers as fast as possible and make sure they are queued */
-    @Test
-    fun testQueueBuffers() {
-        val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
-            scenarioRule.getScenario().onActivity {
-                it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
-                        R8G8B8A8_UNORM)
-                for (i in 0..100) {
-                    it.mSurfaceProxy.ANativeWindowLock()
-                    it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-                }
-                it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */)
-            }
-        }
-        for (frameNumber in 1..100) {
-            assertThat(trace).layer("SurfaceView", frameNumber.toLong())
-        }
-    }
-
-    companion object {
-        private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
-        private const val R8G8B8A8_UNORM = 1
-
-        @JvmStatic
-        @Parameterized.Parameters(name = "blast={0}")
-        fun data(): Collection<Array<Any>> {
-            return listOf(
-                    arrayOf(false), // First test:  submit buffers via bufferqueue
-                    arrayOf(true)   // Second test: submit buffers via blast adapter
-            )
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
new file mode 100644
index 0000000..093c312
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.test
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.provider.Settings
+import androidx.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.rules.TestName
+import org.junit.runners.Parameterized
+import kotlin.properties.Delegates
+
+open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
+    private var mInitialBlastConfig by Delegates.notNull<Boolean>()
+
+    val instrumentation: Instrumentation
+        get() = InstrumentationRegistry.getInstrumentation()
+
+    @get:Rule
+    var mName = TestName()
+
+    @Before
+    open fun setup() {
+        mInitialBlastConfig = getBlastAdapterSvEnabled()
+        setBlastAdapterSvEnabled(useBlastAdapter)
+    }
+
+    @After
+    open fun teardown() {
+        setBlastAdapterSvEnabled(mInitialBlastConfig)
+    }
+
+    private fun getBlastAdapterSvEnabled(): Boolean {
+        return Settings.Global.getInt(instrumentation.context.contentResolver,
+                "use_blast_adapter_sv", 0) != 0
+    }
+
+    private fun setBlastAdapterSvEnabled(enable: Boolean) {
+        Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
+                if (enable) 1 else 0)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "blast={0}")
+        fun data(): Collection<Array<Any>> {
+            return listOf(
+                    arrayOf(false), // First test:  submit buffers via bufferqueue
+                    arrayOf(true)   // Second test: submit buffers via blast adapter
+            )
+        }
+
+        const val R8G8B8A8_UNORM = 1
+        val defaultBufferSize = Point(640, 480)
+
+        // system/window.h definitions
+        enum class ScalingMode() {
+            FREEZE, // = 0
+            SCALE_TO_WINDOW, // =1
+            SCALE_CROP, // = 2
+            NO_SCALE_CROP // = 3
+        }
+
+        // system/window.h definitions
+        enum class Transform(val value: Int) {
+            /* flip source image horizontally */
+            FLIP_H(1),
+            /* flip source image vertically */
+            FLIP_V(2),
+            /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+            ROT_90(4),
+            /* rotate source image 180 degrees */
+            ROT_180(3),
+            /* rotate source image 270 degrees clock-wise */
+            ROT_270(7),
+            /* transforms source by the inverse transform of the screen it is displayed onto. This
+             * transform is applied last */
+            INVERSE_DISPLAY(0x08)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index abccd6cf..fe9deae 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -16,6 +16,7 @@
 package com.android.test.taskembed
 
 import android.app.Instrumentation
+import android.graphics.Point
 import android.graphics.Rect
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.platform.app.InstrumentationRegistry
@@ -93,13 +94,15 @@
 
         // verify buffer size should be changed to expected values.
         assertThat(trace).layer(FIRST_ACTIVITY, frame).also {
-            it.hasLayerSize(firstBounds)
-            it.hasBufferSize(firstBounds)
+            val firstTaskSize = Point(firstBounds.width(), firstBounds.height())
+            it.hasLayerSize(firstTaskSize)
+            it.hasBufferSize(firstTaskSize)
         }
 
         assertThat(trace).layer(SECOND_ACTIVITY, frame).also {
-            it.hasLayerSize(secondBounds)
-            it.hasBufferSize(secondBounds)
+            val secondTaskSize = Point(secondBounds.width(), secondBounds.height())
+            it.hasLayerSize(secondTaskSize)
+            it.hasBufferSize(secondTaskSize)
         }
     }
 
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index dba1856e..70f6386 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -200,7 +200,8 @@
         nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
         nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
 
-        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */,
+                context)
         networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
 
         na.addCapability(NET_CAPABILITY_INTERNET)
@@ -238,7 +239,7 @@
 
         val lp = LinkProperties()
         lp.captivePortalApiUrl = Uri.parse(apiUrl)
-        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
+        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context)
         networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
 
         na.addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 85704d0..2a24d1a 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -72,12 +72,12 @@
     private long mKeepaliveResponseDelay = 0L;
     private Integer mExpectedKeepaliveSlot = null;
 
-    public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
-            throws Exception {
+    public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
+            NetworkCapabilities ncTemplate, Context context) throws Exception {
         final int type = transportToLegacyType(transport);
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
-        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
index eb290dc..938a694 100644
--- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -35,4 +35,5 @@
     private val nextId = AtomicInteger(MAX_NET_ID)
     override fun reserveNetId() = nextId.decrementAndGet()
     override fun releaseNetId(id: Int) = Unit
+    fun peekNextNetId() = nextId.get() - 1
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 7470cd8..2758f61 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -158,7 +158,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InetAddresses;
-import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
 import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -259,7 +259,10 @@
 import org.mockito.Spy;
 import org.mockito.stubbing.Answer;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.net.DatagramSocket;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -322,6 +325,7 @@
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
     private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+    private static final String VPN_IFNAME = "tun10042";
     private static final String TEST_PACKAGE_NAME = "com.android.test.package";
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
@@ -339,6 +343,7 @@
     private INetworkPolicyListener mPolicyListener;
     private WrappedMultinetworkPolicyTracker mPolicyTracker;
     private HandlerThread mAlarmManagerThread;
+    private TestNetIdManager mNetIdManager;
 
     @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
     @Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -617,12 +622,17 @@
         private String mRedirectUrl;
 
         TestNetworkAgentWrapper(int transport) throws Exception {
-            this(transport, new LinkProperties());
+            this(transport, new LinkProperties(), null);
         }
 
         TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
                 throws Exception {
-            super(transport, linkProperties, mServiceContext);
+            this(transport, linkProperties, null);
+        }
+
+        private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+                NetworkCapabilities ncTemplate) throws Exception {
+            super(transport, linkProperties, ncTemplate, mServiceContext);
 
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
@@ -1017,46 +1027,36 @@
         }
     }
 
+    private Set<UidRange> uidRangesForUid(int uid) {
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        return ranges;
+    }
+
     private static Looper startHandlerThreadAndReturnLooper() {
         final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
         handlerThread.start();
         return handlerThread.getLooper();
     }
 
-    private class MockVpn extends Vpn {
-        // TODO : the interactions between this mock and the mock network agent are too
-        // hard to get right at this moment, because it's unclear in which case which
-        // target needs to get a method call or both, and in what order. It's because
-        // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn
-        // parent class of MockVpn agent wants that responsibility.
-        // That being said inside the test it should be possible to make the interactions
-        // harder to get wrong with precise speccing, judicious comments, helper methods
-        // and a few sprinkled assertions.
-
-        private boolean mConnected = false;
+    private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork {
         // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
         // not inherit from NetworkAgent.
         private TestNetworkAgentWrapper mMockNetworkAgent;
-        private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
+        private boolean mAgentRegistered = false;
 
+        private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
         private VpnInfo mVpnInfo;
-        private Network[] mUnderlyingNetworks;
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
                     userId, mock(KeyStore.class));
-        }
-
-        public void setNetworkAgent(TestNetworkAgentWrapper agent) {
-            agent.waitForIdle(TIMEOUT_MS);
-            mMockNetworkAgent = agent;
-            mNetworkAgent = agent.getNetworkAgent();
-            mNetworkCapabilities.set(agent.getNetworkCapabilities());
+            mConfig = new VpnConfig();
         }
 
         public void setUids(Set<UidRange> uids) {
             mNetworkCapabilities.setUids(uids);
-            updateCapabilities(null /* defaultNetwork */);
+            updateCapabilitiesInternal(null /* defaultNetwork */, true);
         }
 
         public void setVpnType(int vpnType) {
@@ -1064,21 +1064,13 @@
         }
 
         @Override
+        public Network getNetwork() {
+            return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
+        }
+
+        @Override
         public int getNetId() {
-            if (mMockNetworkAgent == null) {
-                return NETID_UNSET;
-            }
-            return mMockNetworkAgent.getNetwork().netId;
-        }
-
-        @Override
-        public boolean appliesToUid(int uid) {
-            return mConnected;  // Trickery to simplify testing.
-        }
-
-        @Override
-        protected boolean isCallerEstablishedOwnerLocked() {
-            return mConnected;  // Similar trickery
+            return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
         }
 
         @Override
@@ -1086,41 +1078,94 @@
             return mVpnType;
         }
 
-        private void connect(boolean isAlwaysMetered) {
-            mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
-            mConnected = true;
-            mConfig = new VpnConfig();
+        private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
+                throws Exception {
+            if (mAgentRegistered) throw new IllegalStateException("already registered");
+            setUids(uids);
             mConfig.isMetered = isAlwaysMetered;
+            mInterface = VPN_IFNAME;
+            mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
+                    mNetworkCapabilities);
+            mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
+            mAgentRegistered = true;
+            mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
+            mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
         }
 
-        public void connectAsAlwaysMetered() {
-            connect(true /* isAlwaysMetered */);
+        private void registerAgent(Set<UidRange> uids) throws Exception {
+            registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
         }
 
-        public void connect() {
-            connect(false /* isAlwaysMetered */);
+        private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
+            mMockNetworkAgent.connect(validated, hasInternet, isStrictMode);
+        }
+
+        private void connect(boolean validated) {
+            mMockNetworkAgent.connect(validated);
+        }
+
+        private TestNetworkAgentWrapper getAgent() {
+            return mMockNetworkAgent;
+        }
+
+        public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
+                boolean hasInternet, boolean isStrictMode) throws Exception {
+            mNetworkCapabilities.setOwnerUid(uid);
+            mNetworkCapabilities.setAdministratorUids(new int[]{uid});
+            registerAgent(false, ranges, lp);
+            connect(validated, hasInternet, isStrictMode);
+            waitForIdle();
+        }
+
+        public void establish(LinkProperties lp, int uid, Set<UidRange> ranges) throws Exception {
+            establish(lp, uid, ranges, true, true, false);
+        }
+
+        public void establishForMyUid(LinkProperties lp) throws Exception {
+            final int uid = Process.myUid();
+            establish(lp, uid, uidRangesForUid(uid), true, true, false);
+        }
+
+        public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
+                throws Exception {
+            final int uid = Process.myUid();
+            establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+                    isStrictMode);
+        }
+
+        public void establishForMyUid() throws Exception {
+            establishForMyUid(new LinkProperties());
+        }
+
+        public void sendLinkProperties(LinkProperties lp) {
+            mMockNetworkAgent.sendLinkProperties(lp);
+        }
+
+        private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork,
+                boolean sendToConnectivityService) {
+            if (!mAgentRegistered) return null;
+            super.updateCapabilities(defaultNetwork);
+            // Because super.updateCapabilities will update the capabilities of the agent but
+            // not the mock agent, the mock agent needs to know about them.
+            copyCapabilitiesToNetworkAgent(sendToConnectivityService);
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+
+        private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) {
+            if (null != mMockNetworkAgent) {
+                mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
+                        sendToConnectivityService);
+            }
         }
 
         @Override
         public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
-            if (!mConnected) return null;
-            super.updateCapabilities(defaultNetwork);
-            // Because super.updateCapabilities will update the capabilities of the agent but
-            // not the mock agent, the mock agent needs to know about them.
-            copyCapabilitiesToNetworkAgent();
-            return new NetworkCapabilities(mNetworkCapabilities);
-        }
-
-        private void copyCapabilitiesToNetworkAgent() {
-            if (null != mMockNetworkAgent) {
-                mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
-                        false /* sendToConnectivityService */);
-            }
+            return updateCapabilitiesInternal(defaultNetwork, false);
         }
 
         public void disconnect() {
-            mConnected = false;
-            mConfig = null;
+            if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+            mAgentRegistered = false;
         }
 
         @Override
@@ -1133,18 +1178,6 @@
         private synchronized void setVpnInfo(VpnInfo vpnInfo) {
             mVpnInfo = vpnInfo;
         }
-
-        @Override
-        public synchronized Network[] getUnderlyingNetworks() {
-            if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
-
-            return super.getUnderlyingNetworks();
-        }
-
-        /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
-        private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
-            mUnderlyingNetworks = underlyingNetworks;
-        }
     }
 
     private void mockVpn(int uid) {
@@ -1207,6 +1240,8 @@
 
     @Before
     public void setUp() throws Exception {
+        mNetIdManager = new TestNetIdManager();
+
         mContext = InstrumentationRegistry.getContext();
 
         MockitoAnnotations.initMocks(this);
@@ -1277,7 +1312,7 @@
         doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
         final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
         doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
-        doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+        doReturn(mNetIdManager).when(deps).makeNetIdManager();
         doReturn(mNetworkStack).when(deps).getNetworkStack();
         doReturn(mSystemProperties).when(deps).getSystemProperties();
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
@@ -1335,6 +1370,9 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
+        mMockVpn.disconnect();
+        waitForIdle();
+
         FakeSettingsProvider.clearSettingsProvider();
 
         mCsHandlerThread.quitSafely();
@@ -3218,20 +3256,12 @@
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
 
-        final int uid = Process.myUid();
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true);
-        mMockVpn.connect();
-        defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+        mMockVpn.establishForMyUid();
+        defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        vpnNetworkAgent.disconnect();
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        mMockVpn.disconnect();
+        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
     }
@@ -4808,13 +4838,52 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
+    private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
+        // Easier to implement than a proper "assertSameElements" method that also correctly deals
+        // with duplicates.
+        final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
+        assertEquals(msg, expected.length, actual.length);
+        Set expectedSet = new ArraySet<>(Arrays.asList(expected));
+        assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
+        // actual cannot have duplicates because it's the same length and has the same elements.
+        Set actualSet = new ArraySet<>(Arrays.asList(actual));
+        assertEquals(expectedSet, actualSet);
+    }
+
+    private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+            Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+        ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
+        ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
+                any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
+
+        assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+
+        VpnInfo[] infos = vpnInfosCaptor.getValue();
+        if (vpnUid != null) {
+            assertEquals("Should have exactly one VPN:", 1, infos.length);
+            VpnInfo info = infos[0];
+            assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
+            assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
+            assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+        } else {
+            assertEquals(0, infos.length);
+            return;
+        }
+    }
+
+    private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
+        expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+    }
+
     @Test
     public void testStatsIfacesChanged() throws Exception {
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
 
-        Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
-        Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+        final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+        final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
 
         LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4825,9 +4894,7 @@
         mCellNetworkAgent.connect(false);
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
-                        eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
         // Default network switch should update ifaces.
@@ -4835,32 +4902,24 @@
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
-                        eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
         reset(mStatsService);
 
         // Disconnect should update ifaces.
         mWiFiNetworkAgent.disconnect();
         waitForIdle();
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
-                        eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
         // Metered change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
-                        eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
         mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
-                        eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
         // Captive portal change shouldn't update ifaces
@@ -4874,9 +4933,102 @@
         // Roaming change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
         waitForIdle();
-        verify(mStatsService, atLeastOnce())
-                .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
-                        eq(new VpnInfo[0]));
+        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
+        reset(mStatsService);
+
+        // Test VPNs.
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(VPN_IFNAME);
+
+        mMockVpn.establishForMyUid(lp);
+
+        final Network[] cellAndVpn = new Network[] {
+                mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+        Network[] cellAndWifi = new Network[] {
+                mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+        // A VPN with default (null) underlying networks sets the underlying network's interfaces...
+        expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                new String[]{MOBILE_IFNAME});
+
+        // ...and updates them as the default network switches.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        final Network[] wifiAndVpn = new Network[] {
+                mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+        cellAndWifi = new Network[] {
+                mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+        waitForIdle();
+        assertEquals(wifiLp, mService.getActiveLinkProperties());
+        expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+                new String[]{WIFI_IFNAME});
+        reset(mStatsService);
+
+        // A VPN that sets its underlying networks passes the underlying interfaces, and influences
+        // the default interface sent to NetworkStatsService by virtue of applying to the system
+        // server UID (or, in this test, to the test's UID). This is the reason for sending
+        // MOBILE_IFNAME even though the default network is wifi.
+        // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
+        // applies to the system server UID should not have any bearing on network stats.
+        mService.setUnderlyingNetworksForVpn(onlyCell);
+        waitForIdle();
+        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                new String[]{MOBILE_IFNAME});
+        reset(mStatsService);
+
+        mService.setUnderlyingNetworksForVpn(cellAndWifi);
+        waitForIdle();
+        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+        reset(mStatsService);
+
+        // If an underlying network disconnects, that interface should no longer be underlying.
+        // This doesn't actually work because disconnectAndDestroyNetwork only notifies
+        // NetworkStatsService before the underlying network is actually removed. So the underlying
+        // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This
+        // could result in incorrect data usage measurements if the interface used by the
+        // disconnected network is reused by a system component that does not register an agent for
+        // it (e.g., tethering).
+        mCellNetworkAgent.disconnect();
+        waitForIdle();
+        assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
+        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+
+        // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
+        // network for the VPN...
+        verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
+                any(NetworkState[].class), any() /* anyString() doesn't match null */,
+                argThat(infos -> infos[0].underlyingIfaces.length == 1
+                        && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+        verifyNoMoreInteractions(mStatsService);
+        reset(mStatsService);
+
+        // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
+        // called again, it does. For example, connect Ethernet, but with a low score, such that it
+        // does not become the default network.
+        mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+        mEthernetNetworkAgent.adjustScore(-40);
+        mEthernetNetworkAgent.connect(false);
+        waitForIdle();
+        verify(mStatsService).forceUpdateIfaces(any(Network[].class),
+                any(NetworkState[].class), any() /* anyString() doesn't match null */,
+                argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
+                        && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+        mEthernetNetworkAgent.disconnect();
+        reset(mStatsService);
+
+        // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
+        // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
+        // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which
+        // is probably a performance improvement (though it's very unlikely that a VPN would declare
+        // no underlying networks).
+        // Also, for the same reason as above, the active interface passed in is null.
+        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        waitForIdle();
+        expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
     }
 
@@ -5232,6 +5384,58 @@
     }
 
     @Test
+    public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+
+        mCm.registerNetworkCallback(request, callback);
+
+        // Bring up a VPN that specifies an underlying network that does not exist yet.
+        // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
+        // (and doing so is difficult without using reflection) but it's good to test that the code
+        // behaves approximately correctly.
+        mMockVpn.establishForMyUid(false, true, false);
+        final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+        mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+        callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+        assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+                .hasTransport(TRANSPORT_VPN));
+        assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+                .hasTransport(TRANSPORT_WIFI));
+
+        // Make that underlying network connect, and expect to see its capabilities immediately
+        // reflected in the VPN's capabilities.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+        mWiFiNetworkAgent.connect(false);
+        // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+        // network that has just connected. There appear to be two issues here:
+        // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
+        //    it returns non-null (which happens very early, during handleRegisterNetworkAgent).
+        //    This is not correct because that that point the network is not connected and cannot
+        //    pass any traffic.
+        // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
+        //    before rematching networks.
+        // Given that this scenario can't really happen, this is probably fine for now.
+        callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+                .hasTransport(TRANSPORT_VPN));
+        assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+                .hasTransport(TRANSPORT_WIFI));
+
+        // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+        mWiFiNetworkAgent.disconnect();
+        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expectCapabilitiesThat(mMockVpn, (nc) ->
+                nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+
+        mMockVpn.disconnect();
+        mCm.unregisterNetworkCallback(callback);
+    }
+
+    @Test
     public void testVpnNetworkActive() throws Exception {
         final int uid = Process.myUid();
 
@@ -5265,42 +5469,38 @@
         vpnNetworkCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
+        final Set<UidRange> ranges = uidRangesForUid(uid);
+        mMockVpn.registerAgent(ranges);
+
         // VPN networks do not satisfy the default request and are automatically validated
         // by NetworkMonitor
         assertFalse(NetworkMonitorUtils.isValidationRequired(
-                vpnNetworkAgent.getNetworkCapabilities()));
-        vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
+                mMockVpn.getAgent().getNetworkCapabilities()));
+        mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
 
-        vpnNetworkAgent.connect(false);
-        mMockVpn.connect();
-        mMockVpn.setUnderlyingNetworks(new Network[0]);
+        mMockVpn.connect(false);
+        mService.setUnderlyingNetworksForVpn(new Network[0]);
 
-        genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
-        defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+        vpnNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+        genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
-        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, nc -> null == nc.getUids());
+        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         ranges.clear();
-        vpnNetworkAgent.setUids(ranges);
+        mMockVpn.setUids(ranges);
 
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
 
         // TODO : The default network callback should actually get a LOST call here (also see the
         // comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -5308,19 +5508,18 @@
         // can't currently update their UIDs without disconnecting, so this does not matter too
         // much, but that is the reason the test here has to check for an update to the
         // capabilities instead of the expected LOST then AVAILABLE.
-        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
 
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.setUids(ranges);
 
-        genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+        vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
         // TODO : Here like above, AVAILABLE would be correct, but because this can't actually
         // happen outside of the test, ConnectivityService does not rematch callbacks.
-        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
 
         mWiFiNetworkAgent.disconnect();
 
@@ -5330,13 +5529,13 @@
         vpnNetworkCallback.assertNoCallback();
         defaultCallback.assertNoCallback();
 
-        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
 
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         assertEquals(null, mCm.getActiveNetwork());
 
         mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -5358,20 +5557,13 @@
         defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+        mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
                 false /* isStrictMode */);
-        mMockVpn.connect();
 
         defaultCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
         defaultCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(defaultCallback);
@@ -5390,21 +5582,14 @@
         defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+        mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
                 false /* isStrictMode */);
-        mMockVpn.connect();
 
-        defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        vpnNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        mMockVpn.disconnect();
+        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
         mCm.unregisterNetworkCallback(defaultCallback);
@@ -5422,44 +5607,36 @@
         callback.assertNoCallback();
 
         // Bring up a VPN that has the INTERNET capability, initially unvalidated.
-        final int uid = Process.myUid();
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+        mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
                 false /* isStrictMode */);
-        mMockVpn.connect();
 
         // Even though the VPN is unvalidated, it becomes the default network for our app.
-        callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mMockVpn);
         callback.assertNoCallback();
 
-        assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
-        assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
-        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore());
+        assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore());
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
-        NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
         assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
 
         assertFalse(NetworkMonitorUtils.isValidationRequired(
-                vpnNetworkAgent.getNetworkCapabilities()));
+                mMockVpn.getAgent().getNetworkCapabilities()));
         assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
-                vpnNetworkAgent.getNetworkCapabilities()));
+                mMockVpn.getAgent().getNetworkCapabilities()));
 
         // Pretend that the VPN network validates.
-        vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
-        vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
+        mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid());
         // Expect to see the validated capability, but no other changes, because the VPN is already
         // the default network for the app.
-        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn);
         callback.assertNoCallback();
 
-        vpnNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+        mMockVpn.disconnect();
+        callback.expectCallback(CallbackEntry.LOST, mMockVpn);
         callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
     }
 
@@ -5481,21 +5658,15 @@
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mCellNetworkAgent.connect(true);
 
-        final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.connect();
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+        mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
                 false /* isStrictMode */);
 
-        vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+        vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
                 false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS,
                 nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
 
-        final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(nc.hasTransport(TRANSPORT_VPN));
         assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5517,18 +5688,11 @@
         mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
         vpnNetworkCallback.assertNoCallback();
 
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.connect();
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+        mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
                 false /* isStrictMode */);
 
-        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
-        nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(nc.hasTransport(TRANSPORT_VPN));
         assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5545,7 +5709,7 @@
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5559,7 +5723,7 @@
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5569,7 +5733,7 @@
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5577,27 +5741,27 @@
 
         // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
 
         // Add NOT_SUSPENDED again and observe VPN is no longer suspended.
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
 
         // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5607,7 +5771,7 @@
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5620,7 +5784,7 @@
         // Stop using WiFi. The VPN is suspended again.
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork() });
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5634,7 +5798,7 @@
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5645,14 +5809,14 @@
         // Disconnect cell. Receive update without even removing the dead network from the
         // underlying networks – it's dead anyway. Not metered any more.
         mCellNetworkAgent.disconnect();
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Disconnect wifi too. No underlying networks means this is now metered.
         mWiFiNetworkAgent.disconnect();
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5673,18 +5837,11 @@
         mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
         vpnNetworkCallback.assertNoCallback();
 
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.connect();
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+        mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
                 false /* isStrictMode */);
 
-        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
-        nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(nc.hasTransport(TRANSPORT_VPN));
         assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5696,7 +5853,7 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5706,7 +5863,7 @@
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5718,7 +5875,7 @@
         // Disconnect wifi too. Now we have no default network.
         mWiFiNetworkAgent.disconnect();
 
-        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+        vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5761,18 +5918,10 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // Connect VPN network. By default it is using current default network (Cell).
-        TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        final int uid = Process.myUid();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true);
-        mMockVpn.connect();
-        waitForIdle();
+        mMockVpn.establishForMyUid();
+
         // Ensure VPN is now the active network.
-        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
         // Expect VPN to be metered.
         assertTrue(mCm.isActiveNetworkMetered());
@@ -5783,7 +5932,7 @@
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
         // VPN should still be the active network.
-        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
         // Expect VPN to be unmetered as it should now be using WiFi (new default).
         assertFalse(mCm.isActiveNetworkMetered());
@@ -5801,7 +5950,6 @@
         // VPN without any underlying networks is treated as metered.
         assertTrue(mCm.isActiveNetworkMetered());
 
-        vpnNetworkAgent.disconnect();
         mMockVpn.disconnect();
     }
 
@@ -5822,18 +5970,10 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // Connect VPN network.
-        TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        final int uid = Process.myUid();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true);
-        mMockVpn.connect();
-        waitForIdle();
+        mMockVpn.establishForMyUid();
+
         // Ensure VPN is now the active network.
-        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
         // VPN is using Cell
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mCellNetworkAgent.getNetwork() });
@@ -5873,7 +6013,6 @@
         // VPN without underlying networks is treated as metered.
         assertTrue(mCm.isActiveNetworkMetered());
 
-        vpnNetworkAgent.disconnect();
         mMockVpn.disconnect();
     }
 
@@ -5888,17 +6027,11 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // Connect VPN network.
-        TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        final int uid = Process.myUid();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true);
-        mMockVpn.connectAsAlwaysMetered();
+        mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+                new LinkProperties());
+        mMockVpn.connect(true);
         waitForIdle();
-        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
         // VPN is tracking current platform default (WiFi).
         mService.setUnderlyingNetworksForVpn(null);
@@ -5922,7 +6055,7 @@
 
         assertTrue(mCm.isActiveNetworkMetered());
 
-        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
     }
 
     @Test
@@ -6084,12 +6217,13 @@
     }
 
     /**
-     * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
+     * Make simulated InterfaceConfigParcel for Nat464Xlat to query clat lower layer info.
      */
-    private InterfaceConfiguration getClatInterfaceConfig(LinkAddress la) {
-        InterfaceConfiguration cfg = new InterfaceConfiguration();
-        cfg.setHardwareAddress("11:22:33:44:55:66");
-        cfg.setLinkAddress(la);
+    private InterfaceConfigurationParcel getClatInterfaceConfigParcel(LinkAddress la) {
+        final InterfaceConfigurationParcel cfg = new InterfaceConfigurationParcel();
+        cfg.hwAddr = "11:22:33:44:55:66";
+        cfg.ipv4Addr = la.getAddress().getHostAddress();
+        cfg.prefixLength = la.getPrefixLength();
         return cfg;
     }
 
@@ -6180,8 +6314,8 @@
         reset(mNetworkManagementService);
         reset(mMockNetd);
         reset(mMockDnsResolver);
-        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
-                .thenReturn(getClatInterfaceConfig(myIpv4));
+        when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfigParcel(myIpv4));
 
         // Remove IPv4 address. Expect prefix discovery to be started again.
         cellLp.removeLinkAddress(myIpv4);
@@ -6230,7 +6364,8 @@
                     TYPE_MOBILE);
         }
         reset(mMockNetd);
-
+        when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfigParcel(myIpv4));
         // Change the NAT64 prefix without first removing it.
         // Expect clatd to be stopped and started with the new prefix.
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
@@ -6279,8 +6414,8 @@
         reset(mNetworkManagementService);
         reset(mMockNetd);
         reset(mMockDnsResolver);
-        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
-                .thenReturn(getClatInterfaceConfig(myIpv4));
+        when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfigParcel(myIpv4));
 
         // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -6320,6 +6455,7 @@
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
                 (lp) -> lp.getStackedLinks().size() == 0);
         verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+        verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME);
         verifyNoMoreInteractions(mMockNetd);
         // Clean up.
         mCellNetworkAgent.disconnect();
@@ -6651,34 +6787,21 @@
         waitForIdle();
         assertNull(mService.getProxyForNetwork(null));
 
-        // Set up a VPN network with a proxy
-        final int uid = Process.myUid();
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
-        final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
-        mMockVpn.setUids(ranges);
+        // Connect a VPN network with a proxy.
         LinkProperties testLinkProperties = new LinkProperties();
         testLinkProperties.setHttpProxy(testProxyInfo);
-        vpnNetworkAgent.sendLinkProperties(testLinkProperties);
-        waitForIdle();
-
-        // Connect to VPN with proxy
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        vpnNetworkAgent.connect(true);
-        mMockVpn.connect();
-        waitForIdle();
+        mMockVpn.establishForMyUid(testLinkProperties);
 
         // Test that the VPN network returns a proxy, and the WiFi does not.
-        assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+        assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
         assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
         assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
 
         // Test that the VPN network returns no proxy when it is set to null.
         testLinkProperties.setHttpProxy(null);
-        vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+        mMockVpn.sendLinkProperties(testLinkProperties);
         waitForIdle();
-        assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+        assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork()));
         assertNull(mService.getProxyForNetwork(null));
 
         // Set WiFi proxy and check that the vpn proxy is still null.
@@ -6689,7 +6812,7 @@
 
         // Disconnect from VPN and check that the active network, which is now the WiFi, has the
         // correct proxy setting.
-        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
         waitForIdle();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
@@ -6704,7 +6827,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+        mMockVpn.establish(lp, VPN_UID, vpnRange);
 
         // Connected VPN should have interface rules set up. There are two expected invocations,
         // one during VPN uid update, one during VPN LinkProperties update
@@ -6714,7 +6837,7 @@
         assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
         assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange));
 
-        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
         waitForIdle();
 
         // Disconnected VPN should have interface rules removed
@@ -6731,8 +6854,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
-                lp, Process.SYSTEM_UID, vpnRange);
+        mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
 
         // Legacy VPN should not have interface rules set up
         verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6747,8 +6869,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
-                lp, Process.SYSTEM_UID, vpnRange);
+        mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
 
         // IPv6 unreachable route should not be misinterpreted as a default route
         verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6762,7 +6883,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+        mMockVpn.establish(lp, VPN_UID, vpnRange);
 
         // Connected VPN should have interface rules set up. There are two expected invocations,
         // one during VPN uid update, one during VPN LinkProperties update
@@ -6774,7 +6895,7 @@
         reset(mMockNetd);
         InOrder inOrder = inOrder(mMockNetd);
         lp.setInterfaceName("tun1");
-        vpnNetworkAgent.sendLinkProperties(lp);
+        mMockVpn.sendLinkProperties(lp);
         waitForIdle();
         // VPN handover (switch to a new interface) should result in rules being updated (old rules
         // removed first, then new rules added)
@@ -6787,7 +6908,7 @@
         lp = new LinkProperties();
         lp.setInterfaceName("tun1");
         lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1"));
-        vpnNetworkAgent.sendLinkProperties(lp);
+        mMockVpn.sendLinkProperties(lp);
         waitForIdle();
         // VPN not routing everything should no longer have interface filtering rules
         verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
@@ -6798,7 +6919,7 @@
         lp.setInterfaceName("tun1");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
-        vpnNetworkAgent.sendLinkProperties(lp);
+        mMockVpn.sendLinkProperties(lp);
         waitForIdle();
         // Back to routing all IPv6 traffic should have filtering rules
         verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
@@ -6813,8 +6934,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final UidRange vpnRange = UidRange.createForUser(VPN_USER);
-        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
-                Collections.singleton(vpnRange));
+        mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
 
         reset(mMockNetd);
         InOrder inOrder = inOrder(mMockNetd);
@@ -6823,7 +6943,7 @@
         final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
                 new UidRange(vpnRange.start, APP1_UID - 1),
                 new UidRange(APP1_UID + 1, vpnRange.stop)));
-        vpnNetworkAgent.setUids(newRanges);
+        mMockVpn.setUids(newRanges);
         waitForIdle();
 
         ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
@@ -6964,7 +7084,7 @@
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+        mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
         mMockVpn.setVpnType(vpnType);
 
         final VpnInfo vpnInfo = new VpnInfo();
@@ -7045,19 +7165,6 @@
         mService.getConnectionOwnerUid(getTestConnectionInfo());
     }
 
-    private TestNetworkAgentWrapper establishVpn(
-            LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception {
-        final TestNetworkAgentWrapper
-                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
-        vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.connect();
-        mMockVpn.setUids(vpnRange);
-        vpnNetworkAgent.connect(true);
-        waitForIdle();
-        return vpnNetworkAgent;
-    }
-
     private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
         final PackageInfo packageInfo = new PackageInfo();
         if (hasSystemPermission) {
@@ -7237,22 +7344,28 @@
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
-        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
         // active
         final VpnInfo info = new VpnInfo();
         info.ownerUid = Process.myUid();
-        info.vpnIface = "interface";
+        info.vpnIface = VPN_IFNAME;
         mMockVpn.setVpnInfo(info);
-        mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+
+        mMockVpn.establishForMyUid();
+        waitForIdle();
+
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+
+        assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
         assertTrue(
                 "Active VPN permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
                         Process.myPid(), Process.myUid(), naiWithoutUid,
                         mContext.getOpPackageName()));
 
-        mMockVpn.overrideUnderlyingNetworks(null);
+        assertTrue(mService.setUnderlyingNetworksForVpn(null));
         assertFalse(
                 "VPN shouldn't receive callback on non-underlying network",
                 mService.checkConnectivityDiagnosticsPermissions(
@@ -7273,8 +7386,6 @@
                 Manifest.permission.ACCESS_FINE_LOCATION);
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
-        // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
-        mMockVpn.disconnect();
         assertTrue(
                 "NetworkCapabilities administrator uid permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
@@ -7477,4 +7588,39 @@
 
         mCm.unregisterNetworkCallback(networkCallback);
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        StringWriter stringWriter = new StringWriter();
+
+        mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+
+        assertFalse(stringWriter.toString().isEmpty());
+    }
+
+    @Test
+    public void testRequestsSortedByIdSortsCorrectly() {
+        final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+        final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest genericRequest = new NetworkRequest.Builder()
+                .clearCapabilities().build();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+        mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+        ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+
+        assertTrue(nriOutput.length > 1);
+        for (int i = 0; i < nriOutput.length - 1; i++) {
+            boolean isRequestIdInOrder =
+                    nriOutput[i].mRequests.get(0).requestId
+                            < nriOutput[i + 1].mRequests.get(0).requestId;
+            assertTrue(isRequestIdInOrder);
+        }
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 5046b65..4f65b67 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -29,7 +29,7 @@
 import android.net.ConnectivityManager;
 import android.net.IDnsResolver;
 import android.net.INetd;
-import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -68,7 +68,6 @@
     @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
-    @Mock InterfaceConfiguration mConfig;
     @Mock NetworkAgentInfo mNai;
 
     TestLooper mLooper;
@@ -106,9 +105,10 @@
         when(mNai.connService()).thenReturn(mConnectivity);
         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
         when(mNai.handler()).thenReturn(mHandler);
-
-        when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
-        when(mConfig.getLinkAddress()).thenReturn(ADDR);
+        final InterfaceConfigurationParcel mConfig = new InterfaceConfigurationParcel();
+        when(mNetd.interfaceGetCfg(eq(STACKED_IFACE))).thenReturn(mConfig);
+        mConfig.ipv4Addr = ADDR.getAddress().getHostAddress();
+        mConfig.prefixLength =  ADDR.getPrefixLength();
     }
 
     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
@@ -213,7 +213,7 @@
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
         mLooper.dispatchNext();
 
-        verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+        verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE));
         verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertFalse(c.getValue().getStackedLinks().isEmpty());
         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -353,7 +353,7 @@
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
         mLooper.dispatchNext();
 
-        verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+        verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE));
         verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertFalse(c.getValue().getStackedLinks().isEmpty());
         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index f92c602..b760958 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1568,6 +1568,22 @@
     return true;
   }
 
+  ResourceEntry* ResolveTableEntry(LinkContext* context, ResourceTable* table,
+                                   Reference* reference) {
+    if (!reference || !reference->name) {
+      return nullptr;
+    }
+    auto name_ref = ResourceNameRef(reference->name.value());
+    if (name_ref.package.empty()) {
+      name_ref.package = context->GetCompilationPackage();
+    }
+    const auto search_result = table->FindResource(name_ref);
+    if (!search_result) {
+      return nullptr;
+    }
+    return search_result.value().entry;
+  }
+
   void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
     const xml::Element* application = manifest->root->FindChild("", "application");
     if (!application) {
@@ -1582,22 +1598,13 @@
 
     // Find the icon resource defined within the application.
     const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
-    if (!icon_reference || !icon_reference->name) {
-      return;
-    }
-
-    auto icon_name = ResourceNameRef(icon_reference->name.value());
-    if (icon_name.package.empty()) {
-      icon_name.package = context_->GetCompilationPackage();
-    }
-
-    const auto icon_entry_result = table->FindResource(icon_name);
-    if (!icon_entry_result) {
+    const auto icon_entry = ResolveTableEntry(context_, table, icon_reference);
+    if (!icon_entry) {
       return;
     }
 
     int icon_max_sdk = 0;
-    for (auto& config_value : icon_entry_result.value().entry->values) {
+    for (auto& config_value : icon_entry->values) {
       icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
           ? config_value->config.sdkVersion : icon_max_sdk;
     }
@@ -1608,22 +1615,13 @@
 
     // Find the roundIcon resource defined within the application.
     const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
-    if (!round_icon_reference || !round_icon_reference->name) {
-      return;
-    }
-
-    auto round_icon_name = ResourceNameRef(round_icon_reference->name.value());
-    if (round_icon_name.package.empty()) {
-      round_icon_name.package = context_->GetCompilationPackage();
-    }
-
-    const auto round_icon_entry_result = table->FindResource(round_icon_name);
-    if (!round_icon_entry_result) {
+    const auto round_icon_entry = ResolveTableEntry(context_, table, round_icon_reference);
+    if (!round_icon_entry) {
       return;
     }
 
     int round_icon_max_sdk = 0;
-    for (auto& config_value : round_icon_entry_result.value().entry->values) {
+    for (auto& config_value : round_icon_entry->values) {
       round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
                      ? config_value->config.sdkVersion : round_icon_max_sdk;
     }
@@ -1634,7 +1632,7 @@
     }
 
     // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
-    for (auto& config_value : icon_entry_result.value().entry->values) {
+    for (auto& config_value : icon_entry->values) {
       if (config_value->config.sdkVersion < SDK_O) {
         continue;
       }
@@ -1645,12 +1643,62 @@
                                                      << "\" for round icon compatibility");
 
       auto value = icon_reference->Clone(&table->string_pool);
-      auto round_config_value = round_icon_entry_result.value().entry->FindOrCreateValue(
-          config_value->config, config_value->product);
+      auto round_config_value =
+          round_icon_entry->FindOrCreateValue(config_value->config, config_value->product);
       round_config_value->value.reset(value);
     }
   }
 
+  bool VerifySharedUserId(xml::XmlResource* manifest, ResourceTable* table) {
+    const xml::Element* manifest_el = xml::FindRootElement(manifest->root.get());
+    if (manifest_el == nullptr) {
+      return true;
+    }
+    if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+      return true;
+    }
+    const xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "sharedUserId");
+    if (!attr) {
+      return true;
+    }
+    const auto validate = [&](const std::string& shared_user_id) -> bool {
+      if (util::IsAndroidSharedUserId(context_->GetCompilationPackage(), shared_user_id)) {
+        return true;
+      }
+      DiagMessage error_msg(manifest_el->line_number);
+      error_msg << "attribute 'sharedUserId' in <manifest> tag is not a valid shared user id: '"
+                << shared_user_id << "'";
+      if (options_.manifest_fixer_options.warn_validation) {
+        // Treat the error only as a warning.
+        context_->GetDiagnostics()->Warn(error_msg);
+        return true;
+      }
+      context_->GetDiagnostics()->Error(error_msg);
+      return false;
+    };
+    // If attr->compiled_value is not null, check if it is a ref
+    if (attr->compiled_value) {
+      const auto ref = ValueCast<Reference>(attr->compiled_value.get());
+      if (ref == nullptr) {
+        return true;
+      }
+      const auto shared_user_id_entry = ResolveTableEntry(context_, table, ref);
+      if (!shared_user_id_entry) {
+        return true;
+      }
+      for (const auto& value : shared_user_id_entry->values) {
+        const auto str_value = ValueCast<String>(value->value.get());
+        if (str_value != nullptr && !validate(*str_value->value)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    // Fallback to checking the raw value
+    return validate(attr->value);
+  }
+
   // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
   // to the IArchiveWriter.
   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
@@ -1672,6 +1720,11 @@
     // See (b/34829129)
     AliasAdaptiveIcon(manifest, table);
 
+    // Verify the shared user id here to handle the case of reference value.
+    if (!VerifySharedUserId(manifest, table)) {
+      return false;
+    }
+
     ResourceFileFlattenerOptions file_flattener_options;
     file_flattener_options.keep_raw_values = keep_raw_values;
     file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index fd3a4c0..c03661c 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -163,7 +163,8 @@
   return true;
 }
 
-static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
+static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy,
+                           SourcePathDiagnostics* diag) {
   xml::Attribute* attr = el->FindAttribute({}, "package");
   if (!attr) {
     diag->Error(DiagMessage(el->line_number)
@@ -174,10 +175,16 @@
                 << "attribute 'package' in <manifest> tag must not be a reference");
     return false;
   } else if (!util::IsAndroidPackageName(attr->value)) {
-    diag->Error(DiagMessage(el->line_number)
-                << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
-                << attr->value << "'");
-    return false;
+    DiagMessage error_msg(el->line_number);
+    error_msg << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
+              << attr->value << "'";
+    if (policy == xml::XmlActionExecutorPolicy::kAllowListWarning) {
+      // Treat the error only as a warning.
+      diag->Warn(error_msg);
+    } else {
+      diag->Error(error_msg);
+      return false;
+    }
   }
 
   attr = el->FindAttribute({}, "split");
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index ef33c34..28330db 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -38,6 +38,11 @@
 namespace aapt {
 namespace util {
 
+// Package name and shared user id would be used as a part of the file name.
+// Limits size to 223 and reserves 32 for the OS.
+// See frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+constexpr static const size_t kMaxPackageNameSize = 223;
+
 static std::vector<std::string> SplitAndTransform(
     const StringPiece& str, char sep, const std::function<char(char)>& f) {
   std::vector<std::string> parts;
@@ -169,9 +174,21 @@
 }
 
 bool IsAndroidPackageName(const StringPiece& str) {
+  if (str.size() > kMaxPackageNameSize) {
+    return false;
+  }
   return IsAndroidNameImpl(str) > 1 || str == "android";
 }
 
+bool IsAndroidSharedUserId(const android::StringPiece& package_name,
+                           const android::StringPiece& shared_user_id) {
+  if (shared_user_id.size() > kMaxPackageNameSize) {
+    return false;
+  }
+  return shared_user_id.empty() || IsAndroidNameImpl(shared_user_id) > 1 ||
+         package_name == "android";
+}
+
 bool IsAndroidSplitName(const StringPiece& str) {
   return IsAndroidNameImpl(str) > 0;
 }
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index a956957..c77aca3 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -81,6 +81,7 @@
 // - First character of each component (separated by '.') must be an ASCII letter.
 // - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
 // - Package must contain at least two components, unless it is 'android'.
+// - The maximum package name length is 223.
 bool IsAndroidPackageName(const android::StringPiece& str);
 
 // Tests that the string is a valid Android split name.
@@ -88,6 +89,15 @@
 // - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
 bool IsAndroidSplitName(const android::StringPiece& str);
 
+// Tests that the string is a valid Android shared user id.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+// - Must contain at least two components, unless package name is 'android'.
+// - The maximum shared user id length is 223.
+// - Treat empty string as valid, it's the case of no shared user id.
+bool IsAndroidSharedUserId(const android::StringPiece& package_name,
+                           const android::StringPiece& shared_user_id);
+
 // Converts the class name to a fully qualified class name from the given
 // `package`. Ex:
 //
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index d4e3bec..4ebcb11 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -27,6 +27,17 @@
 
 namespace aapt {
 
+// Test that a max package name size 223 is valid.
+static const std::string kMaxPackageName =
+    "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQux"
+    "SoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O"
+    "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6l";
+// Test that a long package name size 224 is invalid.
+static const std::string kLongPackageName =
+    "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQu"
+    "xSoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O"
+    "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6le";
+
 TEST(UtilTest, TrimOnlyWhitespace) {
   const StringPiece trimmed = util::TrimWhitespace("\n        ");
   EXPECT_TRUE(trimmed.empty());
@@ -108,6 +119,7 @@
   EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing"));
   EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_"));
   EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_"));
+  EXPECT_TRUE(util::IsAndroidPackageName(kMaxPackageName));
 
   EXPECT_FALSE(util::IsAndroidPackageName("android._test"));
   EXPECT_FALSE(util::IsAndroidPackageName("com"));
@@ -116,6 +128,27 @@
   EXPECT_FALSE(util::IsAndroidPackageName(".android"));
   EXPECT_FALSE(util::IsAndroidPackageName(".."));
   EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo"));
+  EXPECT_FALSE(util::IsAndroidPackageName(kLongPackageName));
+}
+
+TEST(UtilTest, IsAndroidSharedUserId) {
+  EXPECT_TRUE(util::IsAndroidSharedUserId("android", "foo"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "android.test"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_thing"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.testing_thing_"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_99_"));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", ""));
+  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", kMaxPackageName));
+
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android._test"));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "com"));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "_android"));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android."));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", ".android"));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", ".."));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "cøm.foo"));
+  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", kLongPackageName));
 }
 
 TEST(UtilTest, FullyQualifiedClassName) {
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
index fab17c9..ea42d26 100644
--- a/tools/aapt2/xml/XmlActionExecutor.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -21,23 +21,34 @@
 namespace aapt {
 namespace xml {
 
-static bool wrapper_one(XmlNodeAction::ActionFunc& f, Element* el, SourcePathDiagnostics*) {
+static bool wrapper_one(const XmlNodeAction::ActionFunc& f, Element* el,
+                        const XmlActionExecutorPolicy& policy, SourcePathDiagnostics*) {
   return f(el);
 }
 
-static bool wrapper_two(XmlNodeAction::ActionFuncWithDiag& f, Element* el,
-                        SourcePathDiagnostics* diag) {
+static bool wrapper_two(const XmlNodeAction::ActionFuncWithDiag& f, Element* el,
+                        const XmlActionExecutorPolicy& policy, SourcePathDiagnostics* diag) {
   return f(el, diag);
 }
 
+static bool wrapper_three(const XmlNodeAction::ActionFuncWithPolicyAndDiag& f, Element* el,
+                          const XmlActionExecutorPolicy& policy, SourcePathDiagnostics* diag) {
+  return f(el, policy, diag);
+}
+
 void XmlNodeAction::Action(XmlNodeAction::ActionFunc f) {
-  actions_.emplace_back(std::bind(
-      wrapper_one, std::move(f), std::placeholders::_1, std::placeholders::_2));
+  actions_.emplace_back(std::bind(wrapper_one, std::move(f), std::placeholders::_1,
+                                  std::placeholders::_2, std::placeholders::_3));
 }
 
 void XmlNodeAction::Action(XmlNodeAction::ActionFuncWithDiag f) {
-  actions_.emplace_back(std::bind(
-      wrapper_two, std::move(f), std::placeholders::_1, std::placeholders::_2));
+  actions_.emplace_back(std::bind(wrapper_two, std::move(f), std::placeholders::_1,
+                                  std::placeholders::_2, std::placeholders::_3));
+}
+
+void XmlNodeAction::Action(XmlNodeAction::ActionFuncWithPolicyAndDiag f) {
+  actions_.emplace_back(std::bind(wrapper_three, std::move(f), std::placeholders::_1,
+                                  std::placeholders::_2, std::placeholders::_3));
 }
 
 static void PrintElementToDiagMessage(const Element* el, DiagMessage* msg) {
@@ -51,8 +62,8 @@
 bool XmlNodeAction::Execute(XmlActionExecutorPolicy policy, std::vector<StringPiece>* bread_crumb,
                             SourcePathDiagnostics* diag, Element* el) const {
   bool error = false;
-  for (const ActionFuncWithDiag& action : actions_) {
-    error |= !action(el, diag);
+  for (const ActionFuncWithPolicyAndDiag& action : actions_) {
+    error |= !action(el, policy, diag);
   }
 
   for (Element* child_el : el->GetChildElements()) {
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index a0ad1da..78c4334 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -49,6 +49,8 @@
 // holds XmlNodeActions for child XML nodes.
 class XmlNodeAction {
  public:
+  using ActionFuncWithPolicyAndDiag =
+      std::function<bool(Element*, XmlActionExecutorPolicy, SourcePathDiagnostics*)>;
   using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>;
   using ActionFunc = std::function<bool(Element*)>;
 
@@ -61,6 +63,7 @@
   // Add an action to be performed at this XmlNodeAction.
   void Action(ActionFunc f);
   void Action(ActionFuncWithDiag);
+  void Action(ActionFuncWithPolicyAndDiag);
 
  private:
   friend class XmlActionExecutor;
@@ -69,7 +72,7 @@
                SourcePathDiagnostics* diag, Element* el) const;
 
   std::map<std::string, XmlNodeAction> map_;
-  std::vector<ActionFuncWithDiag> actions_;
+  std::vector<ActionFuncWithPolicyAndDiag> actions_;
 };
 
 // Allows the definition of actions to execute at specific XML elements defined by their hierarchy.
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index d235c80..dc96df6 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -89,8 +89,6 @@
 rule android.util.LocalLog* com.android.wifi.x.@0
 rule android.util.Rational* com.android.wifi.x.@0
 
-rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0
-
 # Use our statically linked bouncy castle library
 rule org.bouncycastle.** com.android.wifi.x.@0
 # Use our statically linked protobuf library
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index e9d1a00..9f9d7f3 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -538,7 +538,7 @@
         if (!SdkLevel.isAtLeastS()) {
             throw new UnsupportedOperationException();
         }
-        return mChannels;
+        return mChannels.clone();
     }
 
     /**