Merge "Allow requested installer package name to be null"
diff --git a/Android.bp b/Android.bp
index 8926372..a7ce917 100644
--- a/Android.bp
+++ b/Android.bp
@@ -266,11 +266,19 @@
 }
 
 filegroup {
+    name: "framework-updatable-sources",
+    srcs: [
+        ":framework-sdkext-sources",
+        ":updatable-media-srcs",
+    ]
+}
+
+filegroup {
     name: "framework-all-sources",
     srcs: [
         ":framework-mime-sources",
         ":framework-non-updatable-sources",
-        ":updatable-media-srcs",
+        ":framework-updatable-sources",
     ],
 }
 
@@ -319,12 +327,14 @@
         "android.hardware.cas-V1.1-java",
         "android.hardware.cas-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
+        "android.hardware.gnss-V1.0-java",
         "android.hardware.health-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
         "android.hardware.radio-V1.1-java",
         "android.hardware.radio-V1.2-java",
         "android.hardware.radio-V1.3-java",
         "android.hardware.radio-V1.4-java",
+        "android.hardware.radio-V1.5-java",
         "android.hardware.thermal-V1.0-java-constants",
         "android.hardware.thermal-V1.0-java",
         "android.hardware.thermal-V1.1-java",
@@ -436,6 +446,7 @@
         "services-platform-compat-config",
         "media-provider-platform-compat-config",
         "services-devicepolicy-platform-compat-config",
+        "services-core-platform-compat-config",
     ],
     static_libs: [
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
@@ -576,6 +587,12 @@
     ],
 }
 
+java_library {
+    name: "framework-annotations-lib",
+    srcs: [ ":framework-annotations" ],
+    sdk_version: "current",
+}
+
 filegroup {
     name: "framework-networkstack-shared-srcs",
     srcs: [
@@ -1019,15 +1036,15 @@
 stubs_defaults {
     name: "framework-doc-stubs-default",
     srcs: [
-        ":framework-non-updatable-sources",
         ":framework-mime-sources",
+        ":framework-non-updatable-sources",
+        ":framework-updatable-sources",
         "core/java/**/*.logtags",
         "test-base/src/**/*.java",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
         ":core-current-stubs-source",
         ":core_public_api_files",
-        ":updatable-media-srcs",
         "test-mock/src/**/*.java",
         "test-runner/src/**/*.java",
     ],
@@ -1085,12 +1102,12 @@
     name: "metalava-api-stubs-default",
     srcs: [
         ":framework-non-updatable-sources",
+        ":framework-updatable-sources",
         "core/java/**/*.logtags",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
         ":core-current-stubs-source",
         ":core_public_api_files",
-        ":updatable-media-srcs",
         ":ike-api-srcs",
     ],
     libs: ["framework-internal-utils"],
@@ -1620,6 +1637,7 @@
         "core/java/com/android/internal/os/SomeArgs.java",
         "core/java/com/android/internal/util/FastXmlSerializer.java",
         "core/java/com/android/internal/util/HexDump.java",
+        "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
         "core/java/com/android/internal/util/Preconditions.java",
         "core/java/com/android/internal/util/State.java",
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..1e0b58e
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksUiServicesTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/apex/appsearch/framework/java/android/app/TEST_MAPPING b/apex/appsearch/framework/java/android/app/TEST_MAPPING
new file mode 100644
index 0000000..12188f8
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/apex/appsearch/service/java/com/android/server/appsearch"
+    }
+  ]
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 2cd7aa0..4ebafce8 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -21,4 +21,7 @@
     "framework",
     "services.core",
   ],
+  static_libs: [
+    "icing-java-proto-lite",
+  ]
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
new file mode 100644
index 0000000..08811f8
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAppSearchTestCases"
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.appsearch"
+        }
+      ]
+    }
+  ]
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index e611033..5ac922c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -20,9 +20,11 @@
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Pair;
 import android.util.SparseArray;
 
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -42,18 +44,19 @@
 public class FakeIcing {
     private final AtomicInteger mNextDocId = new AtomicInteger();
     private final Map<String, Integer> mUriToDocIdMap = new ArrayMap<>();
-    /** Array of Documents (pair of uri and content) where index into the array is the docId. */
-    private final SparseArray<Pair<String, String>> mDocStore = new SparseArray<>();
+    /** Array of Documents where index into the array is the docId. */
+    private final SparseArray<DocumentProto> mDocStore = new SparseArray<>();
     /** Map of term to posting-list (the set of DocIds containing that term). */
     private final Map<String, Set<Integer>> mIndex = new ArrayMap<>();
 
     /**
      * Inserts a document into the index.
      *
-     * @param uri The globally unique identifier of the document.
-     * @param doc The contents of the document.
+     * @param document The document to insert.
      */
-    public void put(@NonNull String uri, @NonNull String doc) {
+    public void put(@NonNull DocumentProto document) {
+        String uri = document.getUri();
+
         // Update mDocIdMap
         Integer docId = mUriToDocIdMap.get(uri);
         if (docId != null) {
@@ -66,18 +69,10 @@
         mUriToDocIdMap.put(uri, docId);
 
         // Update mDocStore
-        mDocStore.put(docId, Pair.create(uri, doc));
+        mDocStore.put(docId, document);
 
         // Update mIndex
-        String[] words = normalizeString(doc).split("\\s+");
-        for (String word : words) {
-            Set<Integer> postingList = mIndex.get(word);
-            if (postingList == null) {
-                postingList = new ArraySet<>();
-                mIndex.put(word, postingList);
-            }
-            postingList.add(docId);
-        }
+        indexDocument(docId, document);
     }
 
     /**
@@ -87,39 +82,35 @@
      * @return The body of the document, or {@code null} if no such document exists.
      */
     @Nullable
-    public String get(@NonNull String uri) {
+    public DocumentProto get(@NonNull String uri) {
         Integer docId = mUriToDocIdMap.get(uri);
         if (docId == null) {
             return null;
         }
-        Pair<String, String> record = mDocStore.get(docId);
-        if (record == null) {
-            return null;
-        }
-        return record.second;
+        return mDocStore.get(docId);
     }
 
     /**
      * Returns documents containing the given term.
      *
      * @param term A single exact term to look up in the index.
-     * @return The URIs of the matching documents, or an empty {@code List} if no documents match.
+     * @return The matching documents, or an empty {@code List} if no documents match.
      */
     @NonNull
-    public List<String> query(@NonNull String term) {
+    public List<DocumentProto> query(@NonNull String term) {
         String normTerm = normalizeString(term);
         Set<Integer> docIds = mIndex.get(normTerm);
         if (docIds == null || docIds.isEmpty()) {
             return Collections.emptyList();
         }
-        List<String> uris = new ArrayList<>(docIds.size());
+        List<DocumentProto> matches = new ArrayList<>(docIds.size());
         for (int docId : docIds) {
-            Pair<String, String> record = mDocStore.get(docId);
-            if (record != null) {
-                uris.add(record.first);
+            DocumentProto document = mDocStore.get(docId);
+            if (document != null) {
+                matches.add(document);
             }
         }
-        return uris;
+        return matches;
     }
 
     /**
@@ -137,6 +128,39 @@
         }
     }
 
+    private void indexDocument(int docId, DocumentProto document) {
+        for (PropertyProto property : document.getPropertiesList()) {
+            for (String stringValue : property.getStringValuesList()) {
+                String[] words = normalizeString(stringValue).split("\\s+");
+                for (String word : words) {
+                    indexTerm(docId, word);
+                }
+            }
+            for (Long longValue : property.getInt64ValuesList()) {
+                indexTerm(docId, longValue.toString());
+            }
+            for (Double doubleValue : property.getDoubleValuesList()) {
+                indexTerm(docId, doubleValue.toString());
+            }
+            for (Boolean booleanValue : property.getBooleanValuesList()) {
+                indexTerm(docId, booleanValue.toString());
+            }
+            // Intentionally skipping bytes values
+            for (DocumentProto documentValue : property.getDocumentValuesList()) {
+                indexDocument(docId, documentValue);
+            }
+        }
+    }
+
+    private void indexTerm(int docId, String term) {
+        Set<Integer> postingList = mIndex.get(term);
+        if (postingList == null) {
+            postingList = new ArraySet<>();
+            mIndex.put(term, postingList);
+        }
+        postingList.add(docId);
+    }
+
     /** Strips out punctuation and converts to lowercase. */
     private static String normalizeString(String input) {
         return input.replaceAll("\\p{P}", "").toLowerCase(Locale.getDefault());
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index 0274814..5945fb3 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -14,9 +14,12 @@
 
 apex {
     name: "com.android.permission",
-
+    defaults: ["com.android.permission-defaults"],
     manifest: "apex_manifest.json",
+}
 
+apex_defaults {
+    name: "com.android.permission-defaults",
     key: "com.android.permission.key",
     certificate: ":com.android.permission.certificate",
 }
diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp
new file mode 100644
index 0000000..f8978dc
--- /dev/null
+++ b/apex/permission/testing/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+apex {
+    name: "test_com.android.permission",
+    visibility: [
+        "//system/apex/tests",
+    ],
+    defaults: ["com.android.permission-defaults"],
+    manifest: "test_manifest.json",
+    file_contexts: ":com.android.permission-file_contexts",
+    // Test APEX, should never be installed
+    installable: false,
+}
diff --git a/apex/permission/testing/test_manifest.json b/apex/permission/testing/test_manifest.json
new file mode 100644
index 0000000..bc19a9e
--- /dev/null
+++ b/apex/permission/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.permission",
+  "version": 2147483647
+}
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
new file mode 100644
index 0000000..b8dcb90
--- /dev/null
+++ b/apex/sdkext/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+apex {
+    name: "com.android.sdkext",
+    manifest: "manifest.json",
+    java_libs: [ "framework-sdkext" ],
+    key: "com.android.sdkext.key",
+    certificate: ":com.android.sdkext.certificate",
+}
+
+apex_key {
+    name: "com.android.sdkext.key",
+    public_key: "com.android.sdkext.avbpubkey",
+    private_key: "com.android.sdkext.pem",
+}
+
+android_app_certificate {
+    name: "com.android.sdkext.certificate",
+    certificate: "com.android.sdkext",
+}
diff --git a/apex/sdkext/OWNERS b/apex/sdkext/OWNERS
new file mode 100644
index 0000000..feb2742
--- /dev/null
+++ b/apex/sdkext/OWNERS
@@ -0,0 +1 @@
+hansson@google.com
diff --git a/apex/sdkext/com.android.sdkext.avbpubkey b/apex/sdkext/com.android.sdkext.avbpubkey
new file mode 100644
index 0000000..8f47741
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.avbpubkey
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.pem b/apex/sdkext/com.android.sdkext.pem
new file mode 100644
index 0000000..8164601
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAr72pTSavrziDP54AtQZlRclDxJf9HXRZwFRbYx9hWZ4z7ZtO
+pNBDPvPJCiAOVUsILgCQhBUolz2dyLob25Fd0PVp0n9ibIPEQYjTfHjOK40qb57N
+LhEp2ceGiAfsywPSi0TH1PQ6JgbCe/RM4TefI/sj3gYJPka3ksMvylhMIgUVLgME
+kYizhzwHqlLMspB858SioREZdGwcdZrMMIajGFU69Q9ZRDBzhPvxyKhYoObcOtk1
+uVaiE/fNoi3wKGJz2l2vhUuNrQW7MWlVMag+Qes4YACUTk9LZrOVFEJFjWc8xGUi
+ABtfKGs5JgNr/sWnhvifLn8lJuf0/BJmjD+L5QwXYs2cS7gcZJtTM12J94r0Twgw
+wF2lNmIxAE9sYqj5Rh3dIlTPE5vMUECmQEGjIBB/hzT65VxVqSjU/IlS506JTg3p
+IOQtZ15cUzTBpda4jrvqcq6RNVvgBCu2bV5D8Z4z9FUlPyvD+Zq/6lcoJfLtznAs
+G2463hyPAHTGBIcZ5p5bTuGxoAb6ivyqo4b9Qi4yYA6je9HJmuy8T3Mn5JROoeu9
+BH1K54r/mpT4TQPwuKUvRRtBAV2OPHjo+zp0Gd4Y6rxDYxEIdfEae7pQr/QExSPB
+q/QCr9RhixR1mO373LHuja+MxdAxIxugb2HTS61PQo+PbYrhJMcVuxTwJOECAwEA
+AQKCAgAH7ToRrMkH0ji5SdsmTx+KQkW4PFLCXVke/68PjX7KmAQnl3W4oVwnHr/W
+oROEbVn1GTlre7jU+YaAY0SWZrwgjLE1OWGrG1ZizlUbrCdAd6GOX09J4KROml1L
+DXB0x7tbZMLOrCVjSbLD/ITrM6MN8Gnxvbv0/yOQjxU8vzbP4gLOjHxMRCo001RV
+Ll7lPvcjTQ84zJilU6sE8vJ6zdfVZSK/ou2X0cekG+kP7+fvefo8/UcbEPlGhUrV
+IdVPPQGUu90K2hmN0FBdLi8Vik0klAN68Qu/bHwuKbNzsnmIoztucFFUR+fG3u84
+87aPS0L/J3+mjT2Tv6qhJANUGBmrK/h7MkelpKXlRTCITJLX9xP7hfSbJ4f6aLVq
+ZYPPciGxSBbUDgAwvPtOlMDzccg7YsYyiBBO28wh8MN97rePmc0z6nGmjeXhcbCC
+QktG50VYFCyqp5muKgqQmRfRjHFHLWs8GEqgxMeEL3U3HjYfCYr+6E8Sr5OnOBeH
+3buCi1+zgnNYCvbamgY/OJmW7f9h5O31hxmTplc2E1ZuxUGQZthabt1rN3bmNkyf
+KUmPwnIYkDkWBSV5lzyQExfS3/EVvj0EnHhx8faamimNrGo8xCcfnLT3c0WEFVmo
+yIyVRX3EpXJFM2JkeJ21/IEZXTzHSoNxk12CBG8i8lLSflWSMQKCAQEA2ZqVnOuV
+SZfLCUYUUh8Hvhc5zONstfq7ma1Zsttsdaj9t68nLRiBDvLOGnMjDkYZzoSd4fyl
+oy+YqWGBqcqa5kg1NOCH0I46p9d8RcWAfDnB4sqbLgWh70qsvni6igRijmsMDvkA
+U9HeEdPaLCjQ4UXw7GQvN5rRxuRt+OSqV3tV/Pk9JYyYjz7faC8dmbKDrWHHuOvm
+/9y6Xy+L5IgftykNlUeddSCIoMOAadM7BiRjsrHnOYBQ8xBcn0OYafpIswItrgVi
+IrsPJaBFidx8QYK4MVibyka6U0cm28OocDSPtSk/4jrnCEEhLjFUnWwuMXuBGlrd
+W7wP/muoJqb1VwKCAQEAzsAT90kkOCvAcrfGRE3KkUjwWAsQyP8u2+27JIQPqrpW
+GfWAzJXFt80TSp0Zf/Lrq3/SQ9n4AaL4K9dcMoreedoQN9C9JI7zPtZAWNrJVUcV
+dq2gZjBQ78+oK7uQgvFNWxga5D+Nh+Y+9Tp537fc5HIh0Y13PgsxxPk2OnZJTvLX
+HM5H7Aua9ssmqChsrKihuUsDSPozfBz+H7FNHEdKMqVLqJJSK6m0uMxuLovdVfka
+5S7iBMjEGZc46Iz3ckE0pdOiQLooNqfEQqFe5Uou/KZxxKI1NW25rEEBTVyQWt+2
+BNUCfKP7noZ45u5sUY3eJrgI7BrPEDbNS81WYaLchwKCAQA8Q4mHyd6wYO+EA/qA
+u8NDK9+AFMP4qhXme5HJ7Obetwx9IG7zGEQ1xZy6yoQ84cEn5qZq/bNJvFbFIhHs
+2gWIHRtPJ5e1dI5eCVmLYSUyQjSmAIJ1fm3YfY/VuE3BB3HcC11tkBw9GnQr78YO
+UMd4fAw7C4vgFGpgcMbcFUfvrmKkCsqaaZOeqETq75F9DWlWTSwo1HxHA/RBhENz
+6RcPfLkcTJcY5wevrjUUGcHQ86cAyDBHRngkuLVODkRZpU0Y9lN8TFVfVPre6sIX
+ag6nffJRCD8tB+V2RtBGMKunV4ctHt1oY/Oz34W260aJynoIjjG1ANEpJK4xQdNx
+0O9FAoIBAQCz2AGGKemHswdEwveEkuaSWpA3Bekj7lYkmTchHH9EU7JyAkx3qhDD
+QXB2hxGXawf1tsqAmypQwiJ+gGeCz6mW9UkGRF1DX9XX4yc2I5rew2a4RXAxc/Xz
+pP70i8O5I43Wn7FEusOyY2aAis1Y/eb4EQ+56QTAw5wXa3DwidRbCIJ2XDnT6oRy
+CWUnAYMG7ek/9TB2Wq5OWCn2B5S79IdmZsLZb+5qbMT3u1xcwO1Xy8jJc27IGpv6
+ZsDqCTV1/aJ+XQnWpBg28tiV3Sle6pjUzTRJh5AhWcEZRbKMSOiJI/CBY4k2Qq6t
+xuuEdgFjL7T+mTepqehUglcyiPuLEtAhAoIBAQDDQ5pTFOlunfYzm+CIvvImAgy7
+vrEabJYABATytAbXRMMbrKoEdU2ApEDyEW7PgysDnYLAZ+icObnJlBTYvNdlWFly
+XiviGVfpjFWDT9U/gUwFQu2lfEjoiivoGS92USHUy4UMVHiiznnm20VLLkgd3xna
+HUNSDdHIEgzOTmDsKJwMfA9zGckx23JJimPR5ekv6vr6QllYtECs4lTC1gVQAp2f
+5daxHRbkmO6gw1RgQADLkAnYz3aI1jNuHm5VyAZGt/d3JCtZ3Wwwejll8uJ4J09G
+oEtqyY9RVeHK50bLO4lyAXFiE+J6qqXjsGC20cpxeZYW5llMY/dhA6WV4YXV
+-----END RSA PRIVATE KEY-----
diff --git a/apex/sdkext/com.android.sdkext.pk8 b/apex/sdkext/com.android.sdkext.pk8
new file mode 100644
index 0000000..ccc0bf4
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.pk8
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.x509.pem b/apex/sdkext/com.android.sdkext.x509.pem
new file mode 100644
index 0000000..45d2ade
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGIzCCBAugAwIBAgIUXuDL7QvzQh7S6rihWz2KRvCFVT0wDQYJKoZIhvcNAQEL
+BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRswGQYDVQQDDBJjb20uYW5kcm9pZC5zZGtleHQxIjAgBgkqhkiG9w0BCQEW
+E2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjAyMTQyNDM0WhgPNDc1NzEwMjgx
+NDI0MzRaMIGfMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEbMBkGA1UEAwwSY29tLmFuZHJvaWQuc2RrZXh0MSIwIAYJKoZIhvcN
+AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxFvZZ6ES1oqAu1K74/ZxnC3SOhHnLISLBgJEe7DqtdpuNFAwvdVO
+RL/HULhDbjYlOhpU2x3SavDIZZ2lRfiS9Q+M25WftxTRHVjBcpgwbV77TVxPKlAa
+tVN2lUVOY+s4QAVMNIXjC4kCKK/pCQtacH715EtdV47fWdg/Nx4iP/Aord8k3KGI
+9iI2ZOUjaugTRxu5lKRNDrv0bw5rEzyYmDyMud+kR/iS3/5oog57wPE0ffAkZXWE
+p3L2Cejre3ekCizsvVh6EmH6ForKLtL6f0z5Zir1f4R9+YcENspTlJR3pDhg7y3I
+uTQT/iDCtV0l+g2PjGZPEeAQHND3+kDQR7Sno/WC1Nhws6vcu1MdrC+kIh1ewx4y
+8moy/yqb5M98PJDzTSi/AOTB/OiqLXo/T8rjLBmirs9y3fTT6gJ6qXxOWgt8dle9
+7TBfa84Xi8uVY61c+A+YI0nLal7QDPsP3RPv5sJSQ9x9YnweVfD9Q0EOi52sSNu+
+QuN/kcUrMgPgha20VhfH/CkkPDyIp6aZyHHM69MIl+cYEm8vPa5uy3dosuRomT0f
+I4HOBjULDIuj+rIi+Rg3qHvmpuejwZXI/FBNWIhLEUG3ytgksjMaBoYAYflGdrcj
+BQexuF3EO+j4uo7JGjNcaT7wRoCH9gt29VHckDg2qz6VWXrlpmME4UkCAwEAAaNT
+MFEwHQYDVR0OBBYEFISN2nmUHllgPZMZ62U7mU3ZxzlXMB8GA1UdIwQYMBaAFISN
+2nmUHllgPZMZ62U7mU3ZxzlXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggIBAFHIwyBNIVyHXUsDUdcjxfojXQsF/BCL9ehE3pgdkvDfQanaIREWn0nc
+oCFDFkYMRqaXOGC5TKq4OCjXOLsdfODt8HQ3F9J1B0ghQ5tfOdw7xDugNAszqP/Q
+h7kpvqLTycjrqOeZ5KjxEEYtP/KlUmALgOKcTcSH+XhWyxhjF4j24T9F2yJRr3/A
+r1NGU/djH953bHKC8OpJ2teUpDLA4TxVp/EhslH2eVigF80c/w74QPLEWkD9zv/4
+YeRg/R5N83zHs99NtlWMIeHfK6fUbzMyaSZtvm+jK20tkByQb/OQRed+drk25MtL
+68IRvxqri367qRScdpTZbu0ByLO4X8gFdubRUWr+tcO4pZX+DJRVriExbOkU2xhS
+Vtslq23V/hwTuUNm1CXjR70mPS13BTmHrIQDqLoIw/WTQlGh+vxnlAFRIHM3KB2c
+OdzUBu+NcB4aZEd0KKtct600A0DKPr1MQPb5jDq9wEtPSQYwMF0nRFNnXltbrXMd
+4hhwnhKr74fVMUmb+7eQP56XE/Nk4D+npMO54vv1pav+DI2/nxCND9BOLBweY38p
+Tvd2RjesMok0zXuVXiCIu4GEpwo7WkSnv25xrb0Ey2M8QWnGNnCcX7Kv6ip3RdWy
+HiN0G8RJrs/yNEVSDRx8ZhtwTpXVPQxbARbmhNF4/fnolElkmrMP
+-----END CERTIFICATE-----
diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkext/framework/Android.bp
new file mode 100644
index 0000000..b17f0f8
--- /dev/null
+++ b/apex/sdkext/framework/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+filegroup {
+    name: "framework-sdkext-sources",
+    srcs: [
+        "java/**/*.java",
+    ],
+    path: "java",
+}
+
+java_library {
+    name: "framework-sdkext",
+    srcs: [ ":framework-sdkext-sources" ],
+    sdk_version: "system_current",
+    libs: [ "framework-annotations-lib" ],
+    permitted_packages: [ "android.os.ext" ],
+    installable: true,
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
new file mode 100644
index 0000000..c039a82
--- /dev/null
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ext;
+
+import android.annotation.IntDef;
+import android.os.Build.VERSION_CODES;
+import android.os.SystemProperties;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public class SdkExtensions {
+
+    private static final int R_EXTENSION_INT;
+    static {
+        R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
+    }
+
+    /** Values suitable as parameters for {@link #getExtensionVersion(int)}. */
+    @IntDef(value = { VERSION_CODES.R })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SdkVersion {}
+
+    /**
+     * Return the version of the extension to the given SDK.
+     *
+     * @param sdk the SDK version to get the extension version of.
+     * @see SdkVersion
+     * @throws IllegalArgumentException if sdk is not an sdk version with extensions
+     */
+    public static int getExtensionVersion(@SdkVersion int sdk) {
+        if (sdk < VERSION_CODES.R) {
+            throw new IllegalArgumentException();
+        }
+        return R_EXTENSION_INT;
+    }
+
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/package.html b/apex/sdkext/framework/java/android/os/ext/package.html
new file mode 100644
index 0000000..34c1697
--- /dev/null
+++ b/apex/sdkext/framework/java/android/os/ext/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Provides APIs to interface with the SDK extensions.
+</BODY>
+</HTML>
diff --git a/apex/sdkext/framework/tests/Android.bp b/apex/sdkext/framework/tests/Android.bp
new file mode 100644
index 0000000..3d5dbb3
--- /dev/null
+++ b/apex/sdkext/framework/tests/Android.bp
@@ -0,0 +1,10 @@
+android_test {
+    name: "framework-sdkext-tests",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    static_libs: [ "framework-sdkext" ],
+    platform_apis: true,
+}
diff --git a/apex/sdkext/framework/tests/AndroidManifest.xml b/apex/sdkext/framework/tests/AndroidManifest.xml
new file mode 100644
index 0000000..831f132
--- /dev/null
+++ b/apex/sdkext/framework/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.sdkext.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.android.sdkext.tests" />
+
+</manifest>
diff --git a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
new file mode 100644
index 0000000..6885110
--- /dev/null
+++ b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ext;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class SdkExtensionsTest extends TestCase {
+
+    @SmallTest
+    public void testBadArgument() throws Exception {
+        try {
+            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.Q);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+
+        try {
+            SdkExtensions.getExtensionVersion(999999);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+    }
+
+    @SmallTest
+    public void testDefault() throws Exception {
+        int r = SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R);
+        assertTrue(r >= 0);
+    }
+
+}
diff --git a/apex/sdkext/manifest.json b/apex/sdkext/manifest.json
new file mode 100644
index 0000000..048f5c4
--- /dev/null
+++ b/apex/sdkext/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.sdkext",
+  "version": 1
+}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 62c77ef..f11d4f8 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -1163,12 +1163,12 @@
                 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
                         wallClockNanos);
-                e.writeLong(wifiInfo.getTimeStamp());
+                e.writeLong(wifiInfo.getTimeSinceBootMillis());
                 e.writeInt(wifiInfo.getStackState());
-                e.writeLong(wifiInfo.getControllerTxTimeMillis());
-                e.writeLong(wifiInfo.getControllerRxTimeMillis());
-                e.writeLong(wifiInfo.getControllerIdleTimeMillis());
-                e.writeLong(wifiInfo.getControllerEnergyUsed());
+                e.writeLong(wifiInfo.getControllerTxDurationMillis());
+                e.writeLong(wifiInfo.getControllerRxDurationMillis());
+                e.writeLong(wifiInfo.getControllerIdleDurationMillis());
+                e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
                 pulledData.add(e);
             } catch (RemoteException e) {
                 Slog.e(TAG,
@@ -2183,6 +2183,20 @@
                         e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
                         e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
                         e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+
+                        String perm = AppOpsManager.opToPermission(op.getOpCode());
+                        if (perm == null) {
+                            e.writeBoolean(false);
+                        } else {
+                            PermissionInfo permInfo;
+                            try {
+                                permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0);
+                                e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS);
+                            } catch (PackageManager.NameNotFoundException exception) {
+                                e.writeBoolean(false);
+                            }
+                        }
+
                         pulledData.add(e);
                     }
                 }
diff --git a/api/current.txt b/api/current.txt
index b1eac59..b8e4531 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1865,6 +1865,7 @@
     field public static final int accessibilityActionPageLeft = 16908360; // 0x1020048
     field public static final int accessibilityActionPageRight = 16908361; // 0x1020049
     field public static final int accessibilityActionPageUp = 16908358; // 0x1020046
+    field public static final int accessibilityActionPressAndHold = 16908362; // 0x102004a
     field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
     field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
     field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
@@ -11556,8 +11557,11 @@
     field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
     field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
     field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5
+    field public static final int STATUS_FAILURE_ILLEGAL_STATE = 9; // 0x9
     field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
     field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
+    field public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8; // 0x8
+    field public static final int STATUS_FAILURE_SECURITY = 10; // 0xa
     field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
     field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
     field public static final int STATUS_SUCCESS = 0; // 0x0
@@ -11579,6 +11583,7 @@
     method public void removeChildSessionId(int);
     method public void removeSplit(@NonNull String) throws java.io.IOException;
     method public void setStagingProgress(float);
+    method public void transfer(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
@@ -22956,6 +22961,7 @@
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
     method public double getAutomaticGainControlLevelDb();
+    method @FloatRange(from=0, to=50) public double getBasebandCn0DbHz();
     method @Deprecated public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
     method @Deprecated public double getCarrierPhase();
@@ -22973,6 +22979,7 @@
     method public int getSvid();
     method public double getTimeOffsetNanos();
     method public boolean hasAutomaticGainControlLevelDb();
+    method public boolean hasBasebandCn0DbHz();
     method @Deprecated public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
     method @Deprecated public boolean hasCarrierPhase();
@@ -44290,6 +44297,7 @@
     field public static final int EUTRAN = 3; // 0x3
     field public static final int GERAN = 1; // 0x1
     field public static final int IWLAN = 5; // 0x5
+    field public static final int NGRAN = 6; // 0x6
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int UTRAN = 2; // 0x2
   }
@@ -44362,6 +44370,45 @@
     field public static final int BAND_T810 = 7; // 0x7
   }
 
+  public static final class AccessNetworkConstants.NgranBands {
+    field public static final int BAND_1 = 1; // 0x1
+    field public static final int BAND_12 = 12; // 0xc
+    field public static final int BAND_2 = 2; // 0x2
+    field public static final int BAND_20 = 20; // 0x14
+    field public static final int BAND_25 = 25; // 0x19
+    field public static final int BAND_257 = 257; // 0x101
+    field public static final int BAND_258 = 258; // 0x102
+    field public static final int BAND_260 = 260; // 0x104
+    field public static final int BAND_261 = 261; // 0x105
+    field public static final int BAND_28 = 28; // 0x1c
+    field public static final int BAND_3 = 3; // 0x3
+    field public static final int BAND_34 = 34; // 0x22
+    field public static final int BAND_38 = 38; // 0x26
+    field public static final int BAND_39 = 39; // 0x27
+    field public static final int BAND_40 = 40; // 0x28
+    field public static final int BAND_41 = 41; // 0x29
+    field public static final int BAND_5 = 5; // 0x5
+    field public static final int BAND_50 = 50; // 0x32
+    field public static final int BAND_51 = 51; // 0x33
+    field public static final int BAND_66 = 66; // 0x42
+    field public static final int BAND_7 = 7; // 0x7
+    field public static final int BAND_70 = 70; // 0x46
+    field public static final int BAND_71 = 71; // 0x47
+    field public static final int BAND_74 = 74; // 0x4a
+    field public static final int BAND_75 = 75; // 0x4b
+    field public static final int BAND_76 = 76; // 0x4c
+    field public static final int BAND_77 = 77; // 0x4d
+    field public static final int BAND_78 = 78; // 0x4e
+    field public static final int BAND_79 = 79; // 0x4f
+    field public static final int BAND_8 = 8; // 0x8
+    field public static final int BAND_80 = 80; // 0x50
+    field public static final int BAND_81 = 81; // 0x51
+    field public static final int BAND_82 = 82; // 0x52
+    field public static final int BAND_83 = 83; // 0x53
+    field public static final int BAND_84 = 84; // 0x54
+    field public static final int BAND_86 = 86; // 0x56
+  }
+
   public static final class AccessNetworkConstants.UtranBand {
     field public static final int BAND_1 = 1; // 0x1
     field public static final int BAND_10 = 10; // 0xa
@@ -44747,6 +44794,7 @@
     method public abstract int getAsuLevel();
     method public abstract int getDbm();
     method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public abstract int getLevel();
+    method public static final int getNumSignalStrengthLevels();
     method public abstract int hashCode();
     field public static final int SIGNAL_STRENGTH_GOOD = 3; // 0x3
     field public static final int SIGNAL_STRENGTH_GREAT = 4; // 0x4
@@ -45990,6 +46038,193 @@
 
 }
 
+package android.telephony.ims {
+
+  public final class ImsReasonInfo implements android.os.Parcelable {
+    ctor public ImsReasonInfo(int, int, @Nullable String);
+    method public int describeContents();
+    method public int getCode();
+    method public int getExtraCode();
+    method @Nullable public String getExtraMessage();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
+    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
+    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
+    field public static final int CODE_CALL_BARRED = 240; // 0xf0
+    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
+    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
+    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
+    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
+    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
+    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
+    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
+    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
+    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
+    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
+    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
+    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
+    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
+    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
+    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
+    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
+    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
+    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
+    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
+    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
+    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
+    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
+    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
+    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
+    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
+    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
+    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
+    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
+    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
+    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
+    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
+    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
+    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
+    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
+    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
+    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
+    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
+    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
+    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
+    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
+    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
+    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
+    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
+    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
+    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
+    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
+    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
+    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
+    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
+    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
+    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
+    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
+    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
+    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
+    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
+    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
+    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
+    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
+    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
+    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
+    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
+    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
+    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
+    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
+    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
+    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
+    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
+    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
+    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
+    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
+    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
+    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
+    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
+    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
+    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
+    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
+    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
+    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
+    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
+    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
+    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
+    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
+    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
+    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
+    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
+    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
+    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
+    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
+    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
+    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
+    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
+    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
+    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
+    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
+    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
+    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
+    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
+    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
+    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
+    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
+    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
+    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
+    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
+    field public static final int CODE_SIP_BUSY = 338; // 0x152
+    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
+    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
+    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
+    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
+    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
+    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
+    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
+    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
+    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
+    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
+    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
+    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
+    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
+    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
+    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
+    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
+    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
+    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
+    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
+    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
+    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
+    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
+    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
+    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
+    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
+    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
+    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
+    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
+    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
+    field public static final int CODE_UNSPECIFIED = 0; // 0x0
+    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
+    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
+    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
+    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
+    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
+    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
+    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
+    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
+    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
+    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
+    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
+    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
+    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
+    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
+    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
+    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
+    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
+  }
+
+}
+
 package android.telephony.mbms {
 
   public class DownloadProgressListener {
@@ -52770,6 +53005,7 @@
     field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
     field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
     field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT";
     field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
     field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
     field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -52837,6 +53073,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PAGE_RIGHT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PAGE_UP;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PRESS_AND_HOLD;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
@@ -55161,12 +55398,12 @@
   }
 
   public class WebView extends android.widget.AbsoluteLayout implements android.view.ViewGroup.OnHierarchyChangeListener android.view.ViewTreeObserver.OnGlobalFocusChangeListener {
-    ctor public WebView(android.content.Context);
-    ctor public WebView(android.content.Context, android.util.AttributeSet);
-    ctor public WebView(android.content.Context, android.util.AttributeSet, int);
-    ctor public WebView(android.content.Context, android.util.AttributeSet, int, int);
-    ctor @Deprecated public WebView(android.content.Context, android.util.AttributeSet, int, boolean);
-    method public void addJavascriptInterface(Object, String);
+    ctor public WebView(@NonNull android.content.Context);
+    ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+    ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+    ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int, int);
+    ctor @Deprecated public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int, boolean);
+    method public void addJavascriptInterface(@NonNull Object, @NonNull String);
     method public boolean canGoBack();
     method public boolean canGoBackOrForward(int);
     method public boolean canGoForward();
@@ -55180,40 +55417,40 @@
     method public void clearMatches();
     method public void clearSslPreferences();
     method @Deprecated public void clearView();
-    method public android.webkit.WebBackForwardList copyBackForwardList();
+    method @NonNull public android.webkit.WebBackForwardList copyBackForwardList();
     method @Deprecated public android.print.PrintDocumentAdapter createPrintDocumentAdapter();
-    method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(String);
-    method public android.webkit.WebMessagePort[] createWebMessageChannel();
+    method @NonNull public android.print.PrintDocumentAdapter createPrintDocumentAdapter(@NonNull String);
+    method @NonNull public android.webkit.WebMessagePort[] createWebMessageChannel();
     method public void destroy();
     method public static void disableWebView();
-    method public void documentHasImages(android.os.Message);
+    method public void documentHasImages(@NonNull android.os.Message);
     method public static void enableSlowWholeDocumentDraw();
-    method public void evaluateJavascript(String, @Nullable android.webkit.ValueCallback<java.lang.String>);
+    method public void evaluateJavascript(@NonNull String, @Nullable android.webkit.ValueCallback<java.lang.String>);
     method @Deprecated @Nullable public static String findAddress(String);
     method @Deprecated public int findAll(String);
-    method public void findAllAsync(String);
+    method public void findAllAsync(@NonNull String);
     method public void findNext(boolean);
     method public void flingScroll(int, int);
     method @Deprecated public void freeMemory();
     method @Nullable public android.net.http.SslCertificate getCertificate();
     method @android.view.ViewDebug.ExportedProperty(category="webview") public int getContentHeight();
     method @Nullable public static android.content.pm.PackageInfo getCurrentWebViewPackage();
-    method public android.graphics.Bitmap getFavicon();
-    method public android.webkit.WebView.HitTestResult getHitTestResult();
+    method @Nullable public android.graphics.Bitmap getFavicon();
+    method @NonNull public android.webkit.WebView.HitTestResult getHitTestResult();
     method @Deprecated @Nullable public String[] getHttpAuthUsernamePassword(String, String);
-    method @android.view.ViewDebug.ExportedProperty(category="webview") public String getOriginalUrl();
+    method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getOriginalUrl();
     method public int getProgress();
     method public boolean getRendererPriorityWaivedWhenNotVisible();
     method public int getRendererRequestedPriority();
     method @NonNull public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
     method @Deprecated @android.view.ViewDebug.ExportedProperty(category="webview") public float getScale();
-    method public android.webkit.WebSettings getSettings();
+    method @NonNull public android.webkit.WebSettings getSettings();
     method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
-    method @android.view.ViewDebug.ExportedProperty(category="webview") public String getTitle();
-    method @android.view.ViewDebug.ExportedProperty(category="webview") public String getUrl();
+    method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getTitle();
+    method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getUrl();
     method @Nullable public android.webkit.WebChromeClient getWebChromeClient();
     method @NonNull public static ClassLoader getWebViewClassLoader();
-    method public android.webkit.WebViewClient getWebViewClient();
+    method @NonNull public android.webkit.WebViewClient getWebViewClient();
     method @NonNull public android.os.Looper getWebViewLooper();
     method @Nullable public android.webkit.WebViewRenderProcess getWebViewRenderProcess();
     method @Nullable public android.webkit.WebViewRenderProcessClient getWebViewRenderProcessClient();
@@ -55222,10 +55459,10 @@
     method public void goForward();
     method public void invokeZoomPicker();
     method public boolean isPrivateBrowsingEnabled();
-    method public void loadData(String, @Nullable String, @Nullable String);
-    method public void loadDataWithBaseURL(@Nullable String, String, @Nullable String, @Nullable String, @Nullable String);
-    method public void loadUrl(String, java.util.Map<java.lang.String,java.lang.String>);
-    method public void loadUrl(String);
+    method public void loadData(@NonNull String, @Nullable String, @Nullable String);
+    method public void loadDataWithBaseURL(@Nullable String, @NonNull String, @Nullable String, @Nullable String, @Nullable String);
+    method public void loadUrl(@NonNull String, @NonNull java.util.Map<java.lang.String,java.lang.String>);
+    method public void loadUrl(@NonNull String);
     method @Deprecated public void onChildViewAdded(android.view.View, android.view.View);
     method @Deprecated public void onChildViewRemoved(android.view.View, android.view.View);
     method @Deprecated public void onGlobalFocusChanged(android.view.View, android.view.View);
@@ -55236,23 +55473,23 @@
     method public boolean pageDown(boolean);
     method public boolean pageUp(boolean);
     method public void pauseTimers();
-    method public void postUrl(String, byte[]);
-    method public void postVisualStateCallback(long, android.webkit.WebView.VisualStateCallback);
-    method public void postWebMessage(android.webkit.WebMessage, android.net.Uri);
+    method public void postUrl(@NonNull String, @NonNull byte[]);
+    method public void postVisualStateCallback(long, @NonNull android.webkit.WebView.VisualStateCallback);
+    method public void postWebMessage(@NonNull android.webkit.WebMessage, @NonNull android.net.Uri);
     method public void reload();
     method public void removeJavascriptInterface(@NonNull String);
     method public void requestFocusNodeHref(@Nullable android.os.Message);
-    method public void requestImageRef(android.os.Message);
-    method @Nullable public android.webkit.WebBackForwardList restoreState(android.os.Bundle);
+    method public void requestImageRef(@NonNull android.os.Message);
+    method @Nullable public android.webkit.WebBackForwardList restoreState(@NonNull android.os.Bundle);
     method public void resumeTimers();
     method @Deprecated public void savePassword(String, String, String);
-    method @Nullable public android.webkit.WebBackForwardList saveState(android.os.Bundle);
-    method public void saveWebArchive(String);
-    method public void saveWebArchive(String, boolean, @Nullable android.webkit.ValueCallback<java.lang.String>);
+    method @Nullable public android.webkit.WebBackForwardList saveState(@NonNull android.os.Bundle);
+    method public void saveWebArchive(@NonNull String);
+    method public void saveWebArchive(@NonNull String, boolean, @Nullable android.webkit.ValueCallback<java.lang.String>);
     method @Deprecated public void setCertificate(android.net.http.SslCertificate);
-    method public static void setDataDirectorySuffix(String);
-    method public void setDownloadListener(android.webkit.DownloadListener);
-    method public void setFindListener(android.webkit.WebView.FindListener);
+    method public static void setDataDirectorySuffix(@NonNull String);
+    method public void setDownloadListener(@Nullable android.webkit.DownloadListener);
+    method public void setFindListener(@Nullable android.webkit.WebView.FindListener);
     method @Deprecated public void setHorizontalScrollbarOverlay(boolean);
     method @Deprecated public void setHttpAuthUsernamePassword(String, String, String, String);
     method public void setInitialScale(int);
@@ -55263,9 +55500,9 @@
     method public static void setSafeBrowsingWhitelist(@NonNull java.util.List<java.lang.String>, @Nullable android.webkit.ValueCallback<java.lang.Boolean>);
     method public void setTextClassifier(@Nullable android.view.textclassifier.TextClassifier);
     method @Deprecated public void setVerticalScrollbarOverlay(boolean);
-    method public void setWebChromeClient(android.webkit.WebChromeClient);
+    method public void setWebChromeClient(@Nullable android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
-    method public void setWebViewClient(android.webkit.WebViewClient);
+    method public void setWebViewClient(@NonNull android.webkit.WebViewClient);
     method public void setWebViewRenderProcessClient(@NonNull java.util.concurrent.Executor, @NonNull android.webkit.WebViewRenderProcessClient);
     method public void setWebViewRenderProcessClient(@Nullable android.webkit.WebViewRenderProcessClient);
     method @Deprecated public boolean shouldDelayChildPressedState();
@@ -55313,8 +55550,8 @@
 
   public class WebView.WebViewTransport {
     ctor public WebView.WebViewTransport();
-    method public android.webkit.WebView getWebView();
-    method public void setWebView(android.webkit.WebView);
+    method @Nullable public android.webkit.WebView getWebView();
+    method public void setWebView(@Nullable android.webkit.WebView);
   }
 
   public class WebViewClient {
diff --git a/api/system-current.txt b/api/system-current.txt
index 7cd31df..9c8bce6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -58,7 +58,7 @@
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
-    field public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
+    field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
     field public static final String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
     field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
@@ -126,6 +126,7 @@
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
+    field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
     field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
     field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -135,6 +136,7 @@
     field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
     field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
+    field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
@@ -213,6 +215,8 @@
     field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
+    field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
+    field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
     field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
@@ -1337,10 +1341,14 @@
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setActiveDevice(@Nullable android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
+    field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
+    field public static final int ACTIVE_DEVICE_AUDIO = 0; // 0x0
+    field public static final int ACTIVE_DEVICE_PHONE_CALL = 1; // 0x1
   }
 
   public static interface BluetoothAdapter.OnMetadataChangedListener {
@@ -4340,7 +4348,7 @@
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
@@ -5274,18 +5282,30 @@
 
   public final class SoftApConfiguration implements android.os.Parcelable {
     method public int describeContents();
+    method public int getBand();
     method @Nullable public android.net.MacAddress getBssid();
+    method public int getChannel();
+    method public int getSecurityType();
     method @Nullable public String getSsid();
     method @Nullable public String getWpa2Passphrase();
+    method public boolean isHiddenSsid();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BAND_2GHZ = 0; // 0x0
+    field public static final int BAND_5GHZ = 1; // 0x1
+    field public static final int BAND_ANY = -1; // 0xffffffff
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
+    field public static final int SECURITY_TYPE_OPEN = 0; // 0x0
+    field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1
   }
 
   public static final class SoftApConfiguration.Builder {
     ctor public SoftApConfiguration.Builder();
     ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
     method @NonNull public android.net.wifi.SoftApConfiguration build();
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
   }
@@ -5305,6 +5325,32 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR;
   }
 
+  public final class WifiActivityEnergyInfo implements android.os.Parcelable {
+    ctor public WifiActivityEnergyInfo(long, int, long, long, long, long, long);
+    method public int describeContents();
+    method public long getControllerEnergyUsedMicroJoules();
+    method public long getControllerIdleDurationMillis();
+    method public long getControllerRxDurationMillis();
+    method public long getControllerScanDurationMillis();
+    method public long getControllerTxDurationMillis();
+    method public int getStackState();
+    method public long getTimeSinceBootMillis();
+    method public boolean isValid();
+    method public void setControllerEnergyUsedMicroJoules(long);
+    method public void setControllerIdleDurationMillis(long);
+    method public void setControllerRxDurationMillis(long);
+    method public void setControllerScanDurationMillis(long);
+    method public void setControllerTxDurationMillis(long);
+    method public void setStackState(int);
+    method public void setTimeSinceBootMillis(long);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiActivityEnergyInfo> CREATOR;
+    field public static final int STACK_STATE_INVALID = 0; // 0x0
+    field public static final int STACK_STATE_STATE_ACTIVE = 1; // 0x1
+    field public static final int STACK_STATE_STATE_IDLE = 3; // 0x3
+    field public static final int STACK_STATE_STATE_SCANNING = 2; // 0x2
+  }
+
   public final class WifiClient implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.net.MacAddress getMacAddress();
@@ -5423,7 +5469,7 @@
   }
 
   public class WifiManager {
-    method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+    method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -5439,8 +5485,9 @@
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
     method public int getVerboseLoggingLevel();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
     method public boolean isApMacRandomizationSupported();
     method public boolean isConnectedMacRandomizationSupported();
@@ -5456,27 +5503,31 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
-    method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+    method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSoftApBackupData(@NonNull byte[]);
     method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
+    method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
-    method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+    method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspot(@Nullable android.net.wifi.SoftApConfiguration);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
-    method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
+    method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void updateWifiUsabilityScore(int, int, int);
     field public static final String ACTION_LINK_CONFIGURATION_CHANGED = "android.net.wifi.LINK_CONFIGURATION_CHANGED";
+    field @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public static final String ACTION_NETWORK_SETTINGS_RESET = "android.net.wifi.action.NETWORK_SETTINGS_RESET";
     field public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW";
     field public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
     field public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE";
@@ -6003,6 +6054,7 @@
 
   public final class BugreportManager {
     method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
+    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
   }
 
@@ -6448,6 +6500,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
@@ -6982,6 +7035,7 @@
     field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+    field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
     field public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
     field public static final String ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_MORE_DEFAULT_APPS_SETTINGS";
     field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS";
@@ -8020,6 +8074,10 @@
     field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
   }
 
+  public final class ConnectionRequest implements android.os.Parcelable {
+    method @Nullable public String getTelecomCallId();
+  }
+
   public abstract class ConnectionService extends android.app.Service {
     method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
   }
@@ -8227,6 +8285,7 @@
     method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
     method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
     method public int getAllPhoneAccountsCount();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
     method public int getCallState();
     method public android.telecom.PhoneAccountHandle getConnectionManager();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
@@ -10027,186 +10086,6 @@
   }
 
   public final class ImsReasonInfo implements android.os.Parcelable {
-    ctor public ImsReasonInfo(int, int, String);
-    method public int describeContents();
-    method public int getCode();
-    method public int getExtraCode();
-    method public String getExtraMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
-    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
-    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
-    field public static final int CODE_CALL_BARRED = 240; // 0xf0
-    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
-    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
-    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
-    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
-    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
-    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
-    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
-    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
-    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
-    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
-    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
-    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
-    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
-    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
-    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
-    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
-    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
-    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
-    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
-    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
-    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
-    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
-    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
-    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
-    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
-    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
-    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
-    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
-    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
-    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
-    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
-    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
-    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
-    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
-    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
-    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
-    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
-    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
-    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
-    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
-    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
-    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
-    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
-    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
-    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
-    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
-    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
-    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
-    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
-    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
-    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
-    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
-    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
-    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
-    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
-    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
-    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
-    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
-    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
-    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
-    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
-    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
-    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
-    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
-    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
-    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
-    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
-    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
-    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
-    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
-    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
-    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
-    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
-    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
-    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
-    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
-    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
-    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
-    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
-    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
-    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
-    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
-    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
-    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
-    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
-    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
-    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
-    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
-    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
-    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
-    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
-    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
-    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
-    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
-    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
-    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
-    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
-    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
-    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
-    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
-    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
-    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
-    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
-    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
-    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
-    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
-    field public static final int CODE_SIP_BUSY = 338; // 0x152
-    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
-    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
-    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
-    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
-    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
-    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
-    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
-    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
-    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
-    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
-    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
-    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
-    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
-    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
-    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
-    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
-    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
-    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
-    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
-    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
-    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
-    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
-    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
-    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
-    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
-    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
-    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
-    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
-    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
-    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
-    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
-    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
-    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
-    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
-    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
-    field public static final int CODE_UNSPECIFIED = 0; // 0x0
-    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
-    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
-    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
-    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
-    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
-    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
-    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
-    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
-    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
-    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
-    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
-    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
-    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
-    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
-    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
-    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
-    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
-    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 73df147..89fdb3f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1086,6 +1086,7 @@
     ctor public GnssMeasurement();
     method public void reset();
     method public void resetAutomaticGainControlLevel();
+    method public void resetBasebandCn0DbHz();
     method @Deprecated public void resetCarrierCycles();
     method public void resetCarrierFrequencyHz();
     method @Deprecated public void resetCarrierPhase();
@@ -1097,6 +1098,7 @@
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
     method public void setAutomaticGainControlLevelInDb(double);
+    method public void setBasebandCn0DbHz(double);
     method @Deprecated public void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
     method @Deprecated public void setCarrierPhase(double);
@@ -1754,6 +1756,7 @@
 
   public final class BugreportManager {
     method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
+    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
   }
 
@@ -2459,6 +2462,7 @@
 
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+    field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
     field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
@@ -2972,6 +2976,23 @@
     field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
   }
 
+  public final class ConnectionRequest implements android.os.Parcelable {
+    method @Nullable public String getTelecomCallId();
+  }
+
+  public static final class ConnectionRequest.Builder {
+    ctor public ConnectionRequest.Builder();
+    method @NonNull public android.telecom.ConnectionRequest build();
+    method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setTelecomCallId(@NonNull String);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int);
+  }
+
   public static class PhoneAccount.Builder {
     method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
   }
@@ -2985,6 +3006,7 @@
   }
 
   public class TelecomManager {
+    method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode();
     method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
@@ -3432,190 +3454,6 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
-  public final class ImsReasonInfo implements android.os.Parcelable {
-    ctor public ImsReasonInfo(int, int, String);
-    method public int describeContents();
-    method public int getCode();
-    method public int getExtraCode();
-    method public String getExtraMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
-    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
-    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
-    field public static final int CODE_CALL_BARRED = 240; // 0xf0
-    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
-    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
-    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
-    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
-    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
-    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
-    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
-    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
-    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
-    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
-    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
-    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
-    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
-    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
-    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
-    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
-    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
-    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
-    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
-    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
-    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
-    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
-    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
-    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
-    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
-    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
-    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
-    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
-    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
-    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
-    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
-    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
-    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
-    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
-    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
-    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
-    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
-    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
-    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
-    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
-    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
-    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
-    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
-    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
-    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
-    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
-    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
-    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
-    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
-    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
-    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
-    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
-    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
-    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
-    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
-    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
-    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
-    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
-    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
-    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
-    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
-    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
-    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
-    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
-    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
-    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
-    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
-    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
-    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
-    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
-    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
-    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
-    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
-    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
-    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
-    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
-    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
-    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
-    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
-    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
-    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
-    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
-    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
-    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
-    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
-    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
-    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
-    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
-    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
-    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
-    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
-    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
-    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
-    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
-    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
-    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
-    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
-    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
-    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
-    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
-    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
-    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
-    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
-    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
-    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
-    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
-    field public static final int CODE_SIP_BUSY = 338; // 0x152
-    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
-    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
-    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
-    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
-    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
-    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
-    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
-    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
-    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
-    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
-    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
-    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
-    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
-    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
-    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
-    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
-    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
-    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
-    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
-    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
-    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
-    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
-    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
-    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
-    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
-    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
-    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
-    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
-    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
-    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
-    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
-    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
-    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
-    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
-    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
-    field public static final int CODE_UNSPECIFIED = 0; // 0x0
-    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
-    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
-    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
-    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
-    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
-    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
-    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
-    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
-    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
-    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
-    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
-    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
-    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
-    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
-    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
-    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
-    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
-    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
-    field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
-  }
-
   public class ImsService extends android.app.Service {
     ctor public ImsService();
     method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -4272,6 +4110,7 @@
     field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
     field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
+    field public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
     field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
   }
 
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index ba85ae6..3aa8187 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -399,6 +399,8 @@
     
 GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double):
     
+GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
+
 GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
     
 GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index e1cb7ca..459520a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1157,10 +1157,10 @@
 
     parseAnimationDesc(*animation);
     if (!preloadZip(*animation)) {
+        releaseAnimation(animation);
         return nullptr;
     }
 
-
     mLoadedFiles.remove(fn);
     return animation;
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 49cc3c4..b9051da 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -6362,7 +6362,8 @@
     optional bool is_preinstalled = 3;
 
     // True if the package is privileged.
-    optional bool is_priv_app = 4;
+    // Starting from Android 11, this boolean is not set and will always be false.
+    optional bool is_priv_app = 4 [deprecated = true];
 }
 
 /**
@@ -6716,6 +6717,9 @@
     // For long-running operations, total duration of the operation
     // while the app was in the background (only for trusted requests)
     optional int64 trusted_background_duration_millis = 9;
+
+    // Whether AppOps is guarded by Runtime permission
+    optional bool is_runtime_permission = 10;
 }
 
 /**
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 91f8a3c..aaab6b4 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -22,11 +22,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -209,6 +212,32 @@
     }
 
     /**
+     * Launch an activity represented by {@link ShortcutInfo} into this container.
+     * <p>The owner of this container must be allowed to access the shortcut information,
+     * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+     * <p>Activity resolved by the provided {@link ShortcutInfo} must have
+     * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+     * launched here. Also, if activity is not owned by the owner of this container, it must allow
+     * embedding and the caller must have permission to embed.
+     * <p>Note: This class must finish initializing and
+     * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+     * this method can be called.
+     *
+     * @param shortcut the shortcut used to launch the activity.
+     * @param options for the activity.
+     * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+     *                     this shortcut.
+     * @see StateCallback
+     * @see LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)
+     *
+     * @hide
+     */
+    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+        mTaskEmbedder.startShortcutActivity(shortcut, options, sourceBounds);
+    }
+
+    /**
      * Launch a new activity into this container.
      * <p>Activity resolved by the provided {@link Intent} must have
      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
new file mode 100644
index 0000000..844e72e
--- /dev/null
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -0,0 +1,428 @@
+/*
+ * 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.app;
+import android.annotation.NonNull;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally
+ * retrieved across interprocess communication. Imagine that you've written a user birthday
+ * information daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface
+ * over binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ *   int month;
+ *   int day;
+ * }
+ * interface IUserBirthdayService {
+ *   Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ *   private final HashMap<Integer, Birthday> mUidToBirthday;
+ *   @Override
+ *   public synchronized Birthday getUserBirthday(int userId) {
+ *     return mUidToBirthday.get(userId);
+ *   }
+ *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *   }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks
+ * like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   public Birthday getUserBirthday(int userId) {
+ *     return GetService("birthdayd").getUserBirthday(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call
+ * to the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays
+ * change infrequently.
+ *
+ * PropertyInvalidatedCache is part of a pattern for optimizing this kind of
+ * information-querying code. Using {@code PropertyInvalidatedCache}, you'd write the client
+ * this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
+ *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ *   private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
+ *     PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ *       @Override
+ *       protected Birthday recompute(Integer userId) {
+ *         return GetService("birthdayd").getUserBirthday(userId);
+ *       }
+ *     };
+ *   public void disableUserBirthdayCache() {
+ *     mBirthdayCache.disableLocal();
+ *   }
+ *   public void invalidateUserBirthdayCache() {
+ *     mBirthdayCache.invalidateCache();
+ *   }
+ *   public Birthday getUserBirthday(int userId) {
+ *     return mBirthdayCache.query(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ *   ...
+ *   public UserBirthdayServiceImpl() {
+ *     ...
+ *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *
+ *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * The call to {@code PropertyInvalidatedCache.invalidateCache()} guarantees that all clients
+ * will re-fetch birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use
+ * of PropertyInvalidatedCache in this idiomatic way introduces no new race conditions.
+ *
+ * PropertyInvalidatedCache has a few other features for doing things like incremental
+ * enhancement of cached values and invalidation of multiple caches (that all share the same
+ * property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In
+ * this local case, there's no IPC, so use of the cache is (depending on exact
+ * circumstance) unnecessary.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ *
+ * {@hide}
+ */
+public abstract class PropertyInvalidatedCache<Query, Result> {
+    private static final long NONCE_UNSET = 0;
+    private static final long NONCE_DISABLED = -1;
+
+    private static final String TAG = "PropertyInvalidatedCache";
+    private static final boolean DEBUG = false;
+    private static final boolean ENABLE = true;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Name of the property that holds the unique value that we use to invalidate the cache.
+     */
+    private final String mPropertyName;
+
+    /**
+     * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the
+     * property exists on the system.
+     */
+    private volatile SystemProperties.Handle mPropertyHandle;
+
+    @GuardedBy("mLock")
+    private final LinkedHashMap<Query, Result> mCache;
+
+    /**
+     * The last value of the {@code mPropertyHandle} that we observed.
+     */
+    @GuardedBy("mLock")
+    private long mLastSeenNonce = NONCE_UNSET;
+
+    /**
+     * Whether we've disabled the cache in this process.
+     */
+    private boolean mDisabled = false;
+
+    /**
+     * Make a new property invalidated cache.
+     *
+     * @param maxEntries Maximum number of entries to cache; LRU discard
+     * @param propertyName Name of the system property holding the cache invalidation nonce
+     */
+    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
+        mPropertyName = propertyName;
+        mCache = new LinkedHashMap<Query, Result>(
+            2 /* start small */,
+            0.75f /* default load factor */,
+            true /* LRU access order */) {
+                @Override
+                protected boolean removeEldestEntry(Map.Entry eldest) {
+                    return size() > maxEntries;
+                }
+            };
+    }
+
+    /**
+     * Forget all cached values.
+     */
+    public final void clear() {
+        synchronized (mLock) {
+            mCache.clear();
+        }
+    }
+
+    /**
+     * Fetch a result from scratch in case it's not in the cache at all.  Called unlocked: may
+     * block. If this function returns null, the result of the cache query is null. There is no
+     * "negative cache" in the query: we don't cache null results at all.
+     */
+    protected abstract Result recompute(Query query);
+
+    /**
+     * Make result up-to-date on a cache hit.  Called unlocked;
+     * may block.
+     *
+     * Return either 1) oldResult itself (the same object, by reference equality), in which
+     * case we just return oldResult as the result of the cache query, 2) a new object, which
+     * replaces oldResult in the cache and which we return as the result of the cache query
+     * after performing another property read to make sure that the result hasn't changed in
+     * the meantime (if the nonce has changed in the meantime, we drop the cache and try the
+     * whole query again), or 3) null, which causes the old value to be removed from the cache
+     * and null to be returned as the result of the cache query.
+     */
+    protected Result refresh(Result oldResult, Query query) {
+        return oldResult;
+    }
+
+    private long getCurrentNonce() {
+        SystemProperties.Handle handle = mPropertyHandle;
+        if (handle == null) {
+            handle = SystemProperties.find(mPropertyName);
+            if (handle == null) {
+                return NONCE_UNSET;
+            }
+            mPropertyHandle = handle;
+        }
+        return handle.getLong(NONCE_UNSET);
+    }
+
+    /**
+     * Disable the use of this cache in this process.
+     */
+    public final void disableLocal() {
+        synchronized (mLock) {
+            mDisabled = true;
+            mCache.clear();
+        }
+    }
+
+    /**
+     * Return whether the cache is disabled in this process.
+     */
+    public final boolean isDisabledLocal() {
+        return mDisabled;
+    }
+
+    /**
+     * Get a value from the cache or recompute it.
+     */
+    public Result query(Query query) {
+        // Let access to mDisabled race: it's atomic anyway.
+        long currentNonce = (ENABLE && !mDisabled) ? getCurrentNonce() : NONCE_DISABLED;
+        for (;;) {
+            if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            String.format("cache %s for %s",
+                                currentNonce == NONCE_DISABLED ? "disabled" : "unset",
+                                query));
+                }
+                return recompute(query);
+            }
+            final Result cachedResult;
+            synchronized (mLock) {
+                if (currentNonce == mLastSeenNonce) {
+                    cachedResult = mCache.get(query);
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                String.format("clearing cache because nonce changed [%s] -> [%s]",
+                                        mLastSeenNonce, currentNonce));
+                    }
+                    mCache.clear();
+                    mLastSeenNonce = currentNonce;
+                    cachedResult = null;
+                }
+            }
+            // Cache hit --- but we're not quite done yet.  A value in the cache might need to
+            // be augmented in a "refresh" operation.  The refresh operation can combine the
+            // old and the new nonce values.  In order to make sure the new parts of the value
+            // are consistent with the old, possibly-reused parts, we check the property value
+            // again after the refresh and do the whole fetch again if the property invalidated
+            // us while we were refreshing.
+            if (cachedResult != null) {
+                final Result refreshedResult = refresh(cachedResult, query);
+                if (refreshedResult != cachedResult) {
+                    if (DEBUG) {
+                        Log.d(TAG, "cache refresh for " + query);
+                    }
+                    final long afterRefreshNonce = getCurrentNonce();
+                    if (currentNonce != afterRefreshNonce) {
+                        currentNonce = afterRefreshNonce;
+                        if (DEBUG) {
+                            Log.d(TAG, "restarting query because nonce changed in refresh");
+                        }
+                        continue;
+                    }
+                    synchronized (mLock) {
+                        if (currentNonce != mLastSeenNonce) {
+                            // Do nothing: cache is already out of date. Just return the value
+                            // we already have: there's no guarantee that the contents of mCache
+                            // won't become invalid as soon as we return.
+                        } else if (refreshedResult == null) {
+                            mCache.remove(query);
+                        } else {
+                            mCache.put(query, refreshedResult);
+                        }
+                    }
+                    return refreshedResult;
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "cache hit for " + query);
+                }
+                return cachedResult;
+            }
+            // Cache miss: make the value from scratch.
+            if (DEBUG) {
+                Log.d(TAG, "cache miss for " + query);
+            }
+            final Result result = recompute(query);
+            synchronized (mLock) {
+                // If someone else invalidated the cache while we did the recomputation, don't
+                // update the cache with a potentially stale result.
+                if (mLastSeenNonce == currentNonce && result != null) {
+                    mCache.put(query, result);
+                }
+            }
+            return result;
+        }
+    }
+
+    // Inner class avoids initialization in processes that don't do any invalidation
+    private static final class NoPreloadHolder {
+        private static final AtomicLong sNextNonce = new AtomicLong((new Random()).nextLong());
+        public static long next() {
+            return sNextNonce.getAndIncrement();
+        }
+    }
+
+    /**
+     * Non-static convenience version of disableSystemWide() for situations in which only a
+     * single PropertyInvalidatedCache is keyed on a particular property value.
+     *
+     * When multiple caches share a single property value, using an instance method on one of
+     * the cache objects to invalidate all of the cache objects becomes confusing and you should
+     * just use the static version of this function.
+     */
+    public final void disableSystemWide() {
+        disableSystemWide(mPropertyName);
+    }
+
+    /**
+     * Disable all caches system-wide that are keyed on {@var name}. This
+     * function is synchronous: caches are invalidated and disabled upon return.
+     *
+     * @param name Name of the cache-key property to invalidate
+     */
+    public static void disableSystemWide(@NonNull String name) {
+        SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+    }
+
+    /**
+     * Non-static convenience version of invalidateCache() for situations in which only a single
+     * PropertyInvalidatedCache is keyed on a particular property value.
+     */
+    public final void invalidateCache() {
+        invalidateCache(mPropertyName);
+    }
+
+    /**
+     * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
+     * {@var name}. This function is synchronous: caches are invalidated upon return.
+     *
+     * @param name Name of the cache-key property to invalidate
+     */
+    public static void invalidateCache(@NonNull String name) {
+        // There's no race here: we don't require that values strictly increase, but instead
+        // only that each is unique in a single runtime-restart session.
+        final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+        if (nonce == NONCE_DISABLED) {
+            if (DEBUG) {
+                Log.d(TAG, "refusing to invalidate disabled cache: " + name);
+            }
+            return;
+        }
+        long newValue;
+        do {
+            newValue = NoPreloadHolder.next();
+        } while (newValue == NONCE_UNSET || newValue == NONCE_DISABLED);
+        final String newValueString = Long.toString(newValue);
+        if (DEBUG) {
+            Log.d(TAG,
+                    String.format("invalidating cache [%s]: [%s] -> [%s]",
+                            name,
+                            nonce,
+                            newValueString));
+        }
+        SystemProperties.set(name, newValueString);
+    }
+}
diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java
index a8dc7bc..79d88fd 100644
--- a/core/java/android/app/TaskEmbedder.java
+++ b/core/java/android/app/TaskEmbedder.java
@@ -25,9 +25,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
@@ -346,6 +349,26 @@
     }
 
     /**
+     * Launch an activity represented by {@link ShortcutInfo} into this container.
+     * <p>The owner of this container must be allowed to access the shortcut information,
+     * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+     *
+     * @param shortcut the shortcut used to launch the activity.
+     * @param options options for the activity.
+     * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+     *                     this shortcut.
+     *
+     * @see #startActivity(Intent)
+     */
+    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+        LauncherApps service =
+                (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+        options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
+        service.startShortcut(shortcut, sourceBounds, options.toBundle());
+    }
+
+    /**
      * Check if container is ready to launch and create {@link ActivityOptions} to target the
      * virtual display.
      */
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index 260c7df..df643831 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -16,6 +16,7 @@
 
 package android.app.timezonedetector;
 
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 
 /**
@@ -32,5 +33,6 @@
  * {@hide}
  */
 interface ITimeZoneDetectorService {
+  void suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
   void suggestPhoneTimeZone(in PhoneTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl
new file mode 100644
index 0000000..d1be86a
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.timezonedetector;
+
+parcelable ManualTimeZoneSuggestion;
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
new file mode 100644
index 0000000..a6b953b
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -0,0 +1,128 @@
+/*
+ * 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.app.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a manual (user provided) source. The value consists of the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
+ * clock when that number was established. The elapsed realtime clock is considered accurate but
+ * volatile, so time signals must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class ManualTimeZoneSuggestion implements Parcelable {
+
+    public static final @NonNull Creator<ManualTimeZoneSuggestion> CREATOR =
+            new Creator<ManualTimeZoneSuggestion>() {
+                public ManualTimeZoneSuggestion createFromParcel(Parcel in) {
+                    return ManualTimeZoneSuggestion.createFromParcel(in);
+                }
+
+                public ManualTimeZoneSuggestion[] newArray(int size) {
+                    return new ManualTimeZoneSuggestion[size];
+                }
+            };
+
+    @NonNull
+    private final String mZoneId;
+    @Nullable
+    private ArrayList<String> mDebugInfo;
+
+    public ManualTimeZoneSuggestion(@NonNull String zoneId) {
+        mZoneId = Objects.requireNonNull(zoneId);
+    }
+
+    private static ManualTimeZoneSuggestion createFromParcel(Parcel in) {
+        String zoneId = in.readString();
+        ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(zoneId);
+        @SuppressWarnings("unchecked")
+        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+        suggestion.mDebugInfo = debugInfo;
+        return suggestion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mZoneId);
+        dest.writeList(mDebugInfo);
+    }
+
+    @NonNull
+    public String getZoneId() {
+        return mZoneId;
+    }
+
+    @NonNull
+    public List<String> getDebugInfo() {
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+    }
+
+    /**
+     * Associates information with the instance that can be useful for debugging / logging. The
+     * information is present in {@link #toString()} but is not considered for
+     * {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    public void addDebugInfo(String... debugInfos) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>();
+        }
+        mDebugInfo.addAll(Arrays.asList(debugInfos));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ManualTimeZoneSuggestion
+                that = (ManualTimeZoneSuggestion) o;
+        return Objects.equals(mZoneId, that.mZoneId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mZoneId);
+    }
+
+    @Override
+    public String toString() {
+        return "ManualTimeSuggestion{"
+                + "mZoneId=" + mZoneId
+                + ", mDebugInfo=" + mDebugInfo
+                + '}';
+    }
+}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index ab404d2..fb5645a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -160,48 +160,114 @@
     public static final int REASON_MAIN_MASK = 0xFF00;
     /** @hide */
     public static final int REASON_MAIN_DEFAULT =   0x0100;
-    /** @hide */
+    /**
+     * The app spent sufficient time in the old bucket without any substantial event so it reached
+     * the timeout threshold to have its bucket lowered.
+     *
+     * @hide
+     */
     public static final int REASON_MAIN_TIMEOUT =   0x0200;
-    /** @hide */
+    /**
+     * The app was used in some way. Look at the REASON_SUB_USAGE_ reason for more details.
+     * @hide
+     */
     public static final int REASON_MAIN_USAGE =     0x0300;
-    /** @hide */
+    /**
+     * Forced by a core UID.
+     * @hide
+     */
     public static final int REASON_MAIN_FORCED =    0x0400;
-    /** @hide */
+    /**
+     * Set by a privileged system app.
+     * @hide
+     */
     public static final int REASON_MAIN_PREDICTED = 0x0500;
 
     /** @hide */
     public static final int REASON_SUB_MASK = 0x00FF;
-    /** @hide */
+    /**
+     * The app was interacted with in some way by the system.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
-    /** @hide */
+    /**
+     * A notification was viewed by the user. This does not mean the user interacted with the
+     * notification.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN  = 0x0002;
-    /** @hide */
+    /**
+     * The app was interacted with in some way by the user. This includes interacting with
+     * notification.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_USER_INTERACTION   = 0x0003;
-    /** @hide */
+    /**
+     * An {@link android.app.Activity} moved to the foreground.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
-    /** @hide */
+    /**
+     * An {@link android.app.Activity} moved to the background.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
-    /** @hide */
+    /**
+     * There was a system update.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYSTEM_UPDATE      = 0x0006;
-    /** @hide */
+    /**
+     * An app is in an elevated bucket because of an active timeout preventing it from being placed
+     * in a lower bucket.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT     = 0x0007;
-    /** @hide */
+    /**
+     * This system package's sync adapter has been used for another package's content provider.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYNC_ADAPTER       = 0x0008;
-    /** @hide */
+    /**
+     * A slice was pinned by an app.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SLICE_PINNED       = 0x0009;
-    /** @hide */
+    /** /**
+     * A slice was pinned by the default launcher or the default assistant.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV  = 0x000A;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby was scheduled when the device wasn't Dozing.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE = 0x000B;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby was scheduled while the device was Dozing.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE = 0x000C;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby started.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
-    /** @hide */
+    /**
+     * A sync operation that is not exempt from app standby was scheduled.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E;
-    /** @hide */
+    /**
+     * A foreground service started.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F;
-    /** @hide */
+    /**
+     * The predicted bucket was restored after the app's temporary elevation to the ACTIVE bucket
+     * ended.
+     * @hide
+     */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9d152a7..89f9cbc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -456,6 +456,37 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface IoCapability {}
 
+    /** @hide */
+    @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO,
+            ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActiveDeviceUse {}
+
+    /**
+     * Use the specified device for audio (a2dp and hearing aid profile)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_AUDIO = 0;
+
+    /**
+     * Use the specified device for phone calls (headset profile and hearing
+     * aid profile)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_PHONE_CALL = 1;
+
+    /**
+     * Use the specified device for a2dp, hearing aid profile, and headset profile
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_ALL = 2;
+
     /**
      * Broadcast Action: The local Bluetooth adapter has started the remote
      * device discovery process.
@@ -1734,6 +1765,41 @@
     }
 
     /**
+     *
+     * @param device is the remote bluetooth device
+     * @param profiles represents the purpose for which we are setting this as the active device.
+     *                 Possible values are:
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setActiveDevice(@Nullable BluetoothDevice device,
+            @ActiveDeviceUse int profiles) {
+        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
+                && profiles != ACTIVE_DEVICE_ALL) {
+            Log.e(TAG, "Invalid profiles param value in setActiveDevice");
+            return false;
+        }
+
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.setActiveDevice(device, profiles);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+
+        return false;
+    }
+
+    /**
      * Connects all enabled and supported bluetooth profiles between the local and remote device
      *
      * @param device is the remote device with which to connect these profiles
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 04e15c7..b740617 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -36,7 +36,7 @@
 
     void close();
     void commit(in IntentSender statusReceiver, boolean forTransferred);
-    void transfer(in String packageName);
+    void transfer(in String packageName, in IntentSender statusReceiver);
     void abandon();
 
     boolean isMultiPackage();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 0c52979..4ab6f3c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -29,6 +29,8 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager.DeleteFlags;
@@ -36,9 +38,11 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.FileBridge;
 import android.os.Handler;
 import android.os.HandlerExecutor;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -67,6 +71,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
 
@@ -176,7 +182,7 @@
      * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
      * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
      * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
-     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
+     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
      * {@link #STATUS_FAILURE_STORAGE}.
      * <p>
      * More information about a status may be available through additional
@@ -316,6 +322,34 @@
      */
     public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
 
+    /**
+     * The transfer failed because a target package can't be found. For example
+     * transferring a session to a non-existing package.
+     * <p>
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+     * missing package.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
+     */
+    public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8;
+
+    /**
+     * The transfer failed because a session is in invalid state. For example
+     * transferring an already committed session.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_ILLEGAL_STATE = 9;
+
+    /**
+     * The transfer failed for security reasons. For example transferring
+     * to a package which does not have INSTALL_PACKAGES permission.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_SECURITY = 10;
+
     private final IPackageInstaller mInstaller;
     private final int mUserId;
     private final String mInstallerPackageName;
@@ -1052,7 +1086,8 @@
         }
 
         /**
-         * Attempt to commit a session that has been {@link #transfer(String) transferred}.
+         * Attempt to commit a session that has been {@link #transfer(String, IntentSender)
+         * transferred}.
          *
          * <p>If the device reboots before the session has been finalized, you may commit the
          * session again.
@@ -1093,6 +1128,38 @@
          *
          * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
          *                    permission.
+         * @param statusReceiver Called when the state of the session changes. Intents sent to this
+         *                       receiver contain {@link #EXTRA_STATUS}. Refer to the individual
+         *                       transfer status codes on how to handle them.
+         *
+         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
+         * @throws SecurityException if called after the session has been committed or abandoned.
+         * @throws SecurityException if the session does not update the original installer
+         * @throws SecurityException if streams opened through
+         *                           {@link #openWrite(String, long, long) are still open.
+         */
+        public void transfer(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+                throws PackageManager.NameNotFoundException {
+            Preconditions.checkNotNull(statusReceiver);
+            Preconditions.checkNotNull(packageName);
+
+            try {
+                mSession.transfer(packageName, statusReceiver);
+            } catch (ParcelableException e) {
+                e.maybeRethrow(PackageManager.NameNotFoundException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Transfer the session to a new owner.
+         * This is a convenience blocking wrapper around {@link #transfer(String, IntentSender)}.
+         * Converts all statuses into exceptions.
+         *
+         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
+         *                    permission.
          *
          * @throws PackageManager.NameNotFoundException if the new owner could not be found.
          * @throws SecurityException if called after the session has been committed or abandoned.
@@ -1104,13 +1171,43 @@
                 throws PackageManager.NameNotFoundException {
             Preconditions.checkNotNull(packageName);
 
+            CompletableFuture<Intent> intentFuture = new CompletableFuture<Intent>();
             try {
-                mSession.transfer(packageName);
+                IIntentSender localSender = new IIntentSender.Stub() {
+                    @Override
+                    public void send(int code, Intent intent, String resolvedType,
+                            IBinder whitelistToken,
+                            IIntentReceiver finishedReceiver, String requiredPermission,
+                            Bundle options) {
+                        intentFuture.complete(intent);
+                    }
+                };
+                transfer(packageName, new IntentSender(localSender));
             } catch (ParcelableException e) {
                 e.maybeRethrow(PackageManager.NameNotFoundException.class);
                 throw new RuntimeException(e);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+            }
+
+            try {
+                Intent intent = intentFuture.get();
+                final int status = intent.getIntExtra(EXTRA_STATUS, Integer.MIN_VALUE);
+                final String statusMessage = intent.getStringExtra(EXTRA_STATUS_MESSAGE);
+                switch (status) {
+                    case STATUS_SUCCESS:
+                        break;
+                    case STATUS_FAILURE_NAME_NOT_FOUND:
+                        throw new PackageManager.NameNotFoundException(statusMessage);
+                    case STATUS_FAILURE_ILLEGAL_STATE:
+                        throw new IllegalStateException(statusMessage);
+                    case STATUS_FAILURE_SECURITY:
+                        throw new SecurityException(statusMessage);
+                    default:
+                        throw new RuntimeException(statusMessage);
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            } catch (ExecutionException e) {
+                throw new RuntimeException(e);
             }
         }
 
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 407e5e6..fb44402 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -30,7 +30,7 @@
 public class RequestQueue {
     private static final String TAG = "RequestQueue";
 
-    private static final long INVALID_FRAME = -1;
+    public static final long INVALID_FRAME = -1;
 
     private BurstHolder mRepeatingRequest = null;
     private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 32411fb..f9a5029 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -948,8 +948,13 @@
                             Log.d(TAG, "Stopped repeating request. Last frame number is " +
                                     lastFrameNumber);
                         }
-                        mDeviceState.setRepeatingRequestError(lastFrameNumber,
-                                burstHolder.getRequestId());
+                        if (lastFrameNumber != RequestQueue.INVALID_FRAME) {
+                            mDeviceState.setRepeatingRequestError(lastFrameNumber,
+                                    burstHolder.getRequestId());
+                        } else {
+                            Log.e(TAG, "Repeating request id: " + burstHolder.getRequestId() +
+                                    " already canceled!");
+                        }
                     }
 
                     if (DEBUG) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 194068c..d95da91 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1006,7 +1006,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
@@ -1135,7 +1135,7 @@
      *
      * {@hide}
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     @UnsupportedAppUsage
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
         return getActiveNetworkInfoForUid(uid, false);
@@ -1370,10 +1370,14 @@
      * The system network validation may be using different strategies to detect captive portals,
      * so this method does not necessarily return a URL used by the system. It only returns a URL
      * that may be relevant for other components trying to detect captive portals.
+     *
      * @hide
+     * @deprecated This API returns URL which is not guaranteed to be one of the URLs used by the
+     *             system.
      */
+    @Deprecated
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String getCaptivePortalServerUrl() {
         try {
             return mService.getCaptivePortalServerUrl();
@@ -2399,6 +2403,7 @@
      * @return an array of 0 or more {@code String} of tethered dhcp ranges.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getTetheredDhcpRanges() {
         try {
             return mService.getTetheredDhcpRanges();
@@ -2978,7 +2983,7 @@
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void setGlobalProxy(ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
@@ -3123,6 +3128,7 @@
      * Get the mobile provisioning url.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String getMobileProvisioningUrl() {
         try {
             return mService.getMobileProvisioningUrl();
@@ -3169,6 +3175,7 @@
 
     /** {@hide} - returns the factory serial number */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkFactory(Messenger messenger, String name) {
         try {
             return mService.registerNetworkFactory(messenger, name);
@@ -3179,6 +3186,7 @@
 
     /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -3196,6 +3204,7 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return NetID corresponding to NetworkAgent.
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc) {
         return registerNetworkAgent(messenger, ni, lp, nc, score, misc,
@@ -3207,6 +3216,7 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return NetID corresponding to NetworkAgent.
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) {
         try {
@@ -4201,7 +4211,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void startCaptivePortalApp(Network network) {
         try {
             mService.startCaptivePortalApp(network);
@@ -4317,6 +4327,7 @@
      * Resets all connectivity manager settings back to factory defaults.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void factoryReset() {
         try {
             mService.factoryReset();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 95815e4..9fed269 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -27,7 +27,7 @@
 import android.server.ServerProtoEnums;
 import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
 import android.service.batterystats.BatteryStatsServiceDumpProto;
-import android.telephony.SignalStrength;
+import android.telephony.CellSignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
@@ -2529,7 +2529,7 @@
                 new String[] {"in", "out", "em", "off"}),
         new BitDescription(HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK,
                 HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, "phone_signal_strength", "Pss",
-                SignalStrength.SIGNAL_STRENGTH_NAMES,
+                new String[] { "none", "poor", "moderate", "good", "great" },
                 new String[] { "0", "1", "2", "3", "4" }),
         new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
                 HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness", "Sb",
@@ -3891,14 +3891,14 @@
         dumpLine(pw, 0 /* uid */, category, SCREEN_BRIGHTNESS_DATA, args);
 
         // Dump signal strength stats
-        args = new Object[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        args = new Object[CellSignalStrength.getNumSignalStrengthLevels()];
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             args[i] = getPhoneSignalStrengthTime(i, rawRealtime, which) / 1000;
         }
         dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_TIME_DATA, args);
         dumpLine(pw, 0 /* uid */, category, SIGNAL_SCANNING_TIME_DATA,
                 getPhoneSignalScanningTime(rawRealtime, which) / 1000);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             args[i] = getPhoneSignalStrengthCount(i, which);
         }
         dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_COUNT_DATA, args);
@@ -4968,7 +4968,7 @@
             "good (-108dBm to -98dBm): ",
             "great (greater than -98dBm): "};
         didOne = false;
-        final int numCellularRxBins = Math.min(SignalStrength.NUM_SIGNAL_STRENGTH_BINS,
+        final int numCellularRxBins = Math.min(CellSignalStrength.getNumSignalStrengthLevels(),
             cellularRxSignalStrengthDescription.length);
         for (int i=0; i<numCellularRxBins; i++) {
             final long time = getPhoneSignalStrengthTime(i, rawRealtime, which);
@@ -8216,7 +8216,7 @@
                 which);
 
         // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
-        for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); ++i) {
             final long pssToken = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
             proto.write(SystemProto.PhoneSignalStrength.NAME, i);
             dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ec39199..15ff69e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -505,6 +505,19 @@
     public static final native void restoreCallingWorkSource(long token);
 
     /**
+     * Mark as being built with VINTF-level stability promise. This API should
+     * only ever be invoked by the build system. It means that the interface
+     * represented by this binder is guaranteed to be kept stable for several
+     * years, and the build system also keeps snapshots of these APIs and
+     * invokes the AIDL compiler to make sure that these snapshots are
+     * backwards compatible. Instead of using this API, use an @VintfStability
+     * interface.
+     *
+     * @hide
+     */
+    public final native void markVintfStability();
+
+    /**
      * Flush any Binder commands pending in the current thread to the kernel
      * driver.  This can be
      * useful to call before performing an operation that may block for a long
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index c5cbad3..a3c2cd8 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.util.Log;
 
@@ -191,6 +192,32 @@
         }
     }
 
+    /**
+     * Requests a bugreport.
+     *
+     * <p>This requests the platform/system to take a bugreport and makes the final bugreport
+     * available to the user. The user may choose to share it with another app, but the bugreport
+     * is never given back directly to the app that requested it.
+     *
+     * @param params           {@link BugreportParams} that specify what kind of a bugreport should
+     *                         be taken, please note that not all kinds of bugreport allow for a
+     *                         progress notification
+     * @param shareTitle       title on the final share notification
+     * @param shareDescription description on the final share notification
+     */
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public void requestBugreport(@NonNull BugreportParams params, @Nullable CharSequence shareTitle,
+            @Nullable CharSequence shareDescription) {
+        try {
+            String title = shareTitle == null ? null : shareTitle.toString();
+            String description = shareDescription == null ? null : shareDescription.toString();
+            ActivityManager.getService().requestBugReportWithDescription(title, description,
+                    params.getMode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private final class DumpstateListener extends IDumpstateListener.Stub {
         private final Executor mExecutor;
         private final BugreportCallback mCallback;
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 4d9ebc2..5b715c0 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -32,7 +32,7 @@
 
 /**
  * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
- * to fire an event after files are accessed or changed by by any process on
+ * to fire an event after files are accessed or changed by any process on
  * the device (including this one).  FileObserver is an abstract class;
  * subclasses must implement the event handler {@link #onEvent(int, String)}.
  *
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ce91c50..dbe8dc3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -46,6 +46,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.view.WindowManager.LayoutParams;
@@ -486,13 +487,13 @@
      *
      * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
      * as the device owner or profile owner can still enable or disable location mode via
-     * {@link DevicePolicyManager#setSecureSetting} when this restriction is on.
+     * {@link DevicePolicyManager#setLocationEnabled} when this restriction is on.
      *
      * <p>The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
-     * @see android.location.LocationManager#isProviderEnabled(String)
+     * @see LocationManager#isLocationEnabled()
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
      * @see #getUserRestrictions()
@@ -2708,6 +2709,21 @@
     }
 
     /**
+     * Returns information for all users on this device. Requires
+     * {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param excludeDying specify if the list should exclude users being
+     *            removed.
+     * @return the list of users that were created.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
+        return getUsers(/*excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
      * Returns information for all users on this device, based on the filtering parameters.
      *
      * @hide
@@ -2723,6 +2739,24 @@
     }
 
     /**
+     * Returns the user handles for all users on this device, based on the filtering parameters.
+     *
+     * @param excludeDying specify if the list should exclude users being removed.
+     * @return the list of user handles.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) {
+        List<UserInfo> users = getUsers(excludeDying);
+        List<UserHandle> result = new ArrayList<>(users.size());
+        for (UserInfo user : users) {
+            result.add(user.getUserHandle());
+        }
+        return result;
+    }
+
+    /**
      * Returns serial numbers of all users on this device.
      *
      * @param excludeDying specify if the list should exclude users being removed.
@@ -3262,21 +3296,6 @@
     }
 
     /**
-     * Returns information for all users on this device. Requires
-     * {@link android.Manifest.permission#MANAGE_USERS} permission.
-     *
-     * @param excludeDying specify if the list should exclude users being
-     *            removed.
-     * @return the list of users that were created.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
-        return getUsers(/*excludePartial= */ true, excludeDying,
-                /* excludePreCreated= */ true);
-    }
-
-    /**
      * Removes a user and all associated data.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userId the integer handle of the user.
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index caa4068..b8c1802 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -22,8 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.NetworkType;
+import android.telephony.CellSignalStrength;
 import android.telephony.ModemActivityInfo;
-import android.telephony.SignalStrength;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -48,7 +48,7 @@
     private long mEnergyConsumedMaMs = 0;
     private long[] mTimeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
     private long[] mTimeInRxSignalStrengthLevelMs =
-            new long[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+            new long[CellSignalStrength.getNumSignalStrengthLevels()];
     private long[] mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS];
     private long mMonitoredRailChargeConsumedMaMs = 0;
 
@@ -354,7 +354,7 @@
     /** @hide **/
     public void setTimeInRxSignalStrengthLevelMicros(@NonNull long[] t) {
         mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(t, 0,
-            Math.min(t.length, SignalStrength.NUM_SIGNAL_STRENGTH_BINS));
+            Math.min(t.length, CellSignalStrength.getNumSignalStrengthLevels()));
         return;
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f627daa..fa8896e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -832,8 +832,7 @@
     /**
      * Activity Action: Show screen for controlling which apps can draw on top of other apps.
      * <p>
-     * In some cases, a matching Activity may not exist, so ensure you
-     * safeguard against this.
+     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
      * <p>
      * Input: Optionally, in versions of Android prior to 11, the Intent's data URI can specify the
      * application package name to directly invoke the management GUI specific to the package name.
@@ -846,6 +845,31 @@
             "android.settings.action.MANAGE_OVERLAY_PERMISSION";
 
     /**
+     * Activity Action: Show screen for controlling if the app specified in the data URI of the
+     * intent can draw on top of other apps.
+     * <p>
+     * Unlike {@link #ACTION_MANAGE_OVERLAY_PERMISSION}, which in Android 11 can't be used to show
+     * a GUI for a specific package, permission {@code android.permission.INTERNAL_SYSTEM_WINDOW} is
+     * needed to start an activity with this intent.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: The Intent's data URI MUST specify the application package name whose ability of
+     * drawing on top of other apps you want to control.
+     * For example "package:com.my.app".
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION =
+            "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
+
+    /**
      * Activity Action: Show screen for controlling which apps are allowed to write/modify
      * system settings.
      * <p>
@@ -4952,7 +4976,6 @@
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET);
             MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
-            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
             MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
             MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
             MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
@@ -9283,16 +9306,6 @@
        @SystemApi
        public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
 
-       /**
-        * Whether the package manager should send package verification broadcasts for verifiers to
-        * review apps prior to installation.
-        * 1 = request apps to be verified prior to installation, if a verifier exists.
-        * 0 = do not verify apps before installation
-        * @hide
-        */
-       @UnsupportedAppUsage
-       public static final String PACKAGE_VERIFIER_ENABLE = "package_verifier_enable";
-
        /** Timeout for package verification.
         * @hide */
        public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index add0316..9e454e6 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4155,20 +4155,6 @@
         public static final String CID = "cid";
 
         /**
-         * Message code. <em>OBSOLETE: merged into SERIAL_NUMBER.</em>
-         * <P>Type: INTEGER</P>
-         * @hide
-         */
-        public static final String V1_MESSAGE_CODE = "message_code";
-
-        /**
-         * Message identifier. <em>OBSOLETE: renamed to SERVICE_CATEGORY.</em>
-         * <P>Type: INTEGER</P>
-         * @hide
-         */
-        public static final String V1_MESSAGE_IDENTIFIER = "message_id";
-
-        /**
          * Service category which represents the general topic of the message.
          * <p>
          * For GSM/UMTS: message identifier (see 3GPP TS 23.041 section 9.4.1.2.2)
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 7e09740..236e5ae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,6 +41,7 @@
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
     public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
+    public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -48,6 +49,7 @@
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+        DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "false");
         DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
         DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index f7077bb..64e15cf 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -224,12 +224,35 @@
     /**
      * Write an event to stats log using the raw format.
      *
-     * @param buffer    The encoded buffer of data to write..
+     * @param buffer    The encoded buffer of data to write.
      * @param size      The number of bytes from the buffer to write.
      * @hide
      */
+    // TODO(b/144935988): Mark deprecated.
     @SystemApi
-    public static native void writeRaw(@NonNull byte[] buffer, int size);
+    public static void writeRaw(@NonNull byte[] buffer, int size) {
+        // TODO(b/144935988): make this no-op once clients have migrated to StatsEvent.
+        writeImpl(buffer, size, 0);
+    }
+
+    /**
+     * Write an event to stats log using the raw format.
+     *
+     * @param buffer    The encoded buffer of data to write.
+     * @param size      The number of bytes from the buffer to write.
+     * @param atomId    The id of the atom to which the event belongs.
+     */
+    private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId);
+
+    /**
+     * Write an event to stats log using the raw format encapsulated in StatsEvent.
+     *
+     * @param statsEvent    The StatsEvent object containing the encoded buffer of data to write.
+     * @hide
+     */
+    public static void write(@NonNull final StatsEvent statsEvent) {
+        writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
+    }
 
     private static void enforceDumpCallingPermission(Context context) {
         context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission.");
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index f4bee57..d01c933 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -67,20 +67,20 @@
     void onActionsChanged(in ParceledListSlice actions);
 
     /**
-     * Called by the window manager to notify the listener to save the reentry fraction,
+     * Called by the window manager to notify the listener to save the reentry fraction and size,
      * typically when an Activity leaves PiP (picture-in-picture) mode to fullscreen.
      * {@param componentName} represents the application component of PiP window
      * while {@param bounds} is the current PiP bounds used to calculate the
-     * reentry snap fraction.
+     * reentry snap fraction and size.
      */
-    void onSaveReentrySnapFraction(in ComponentName componentName, in Rect bounds);
+    void onSaveReentryBounds(in ComponentName componentName, in Rect bounds);
 
     /**
-     * Called by the window manager to notify the listener to reset saved reentry fraction,
+     * Called by the window manager to notify the listener to reset saved reentry fraction and size,
      * typically when an Activity enters PiP (picture-in-picture) mode from fullscreen.
      * {@param componentName} represents the application component of PiP window.
      */
-    void onResetReentrySnapFraction(in ComponentName componentName);
+    void onResetReentryBounds(in ComponentName componentName);
 
     /**
      * Called when the window manager has detected change on DisplayInfo,  or
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 258b1ae..b829c9f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -66,6 +66,22 @@
 interface IWindowManager
 {
     /**
+     * No overridden behavior is provided in terms of fixing rotation to user rotation. Use
+     * other flags to derive the default behavior, such as {@link WindowManagerService#mIsPc}
+     * and {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}.
+     */
+    const int FIXED_TO_USER_ROTATION_DEFAULT = 0;
+    /**
+     * Don't fix display rotation to {@link DisplayRotation#mUserRotation} only. Always allow
+     * other factors to play a role in deciding display rotation.
+     */
+    const int FIXED_TO_USER_ROTATION_DISABLED = 1;
+    /**
+     * Only use {@link DisplayRotation#mUserRotation} as the display rotation.
+     */
+    const int FIXED_TO_USER_ROTATION_ENABLED = 2;
+
+    /**
      * ===== NOTICE =====
      * The first three methods must remain the first three methods. Scripts
      * and tools rely on their transaction number to work properly.
@@ -273,6 +289,11 @@
     boolean isDisplayRotationFrozen(int displayId);
 
     /**
+    *  Sets if display rotation is fixed to user specified value for given displayId.
+    */
+    void setFixedToUserRotation(int displayId, int fixedToUserRotation);
+
+    /**
      * Screenshot the current wallpaper layer, including the whole screen.
      */
     Bitmap screenshotWallpaper();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6458737..db6211d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,6 +161,8 @@
     private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken,
                                                                  int[] allowedConfigs);
     private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken);
+    private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
+            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
     private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
             IBinder displayToken);
@@ -1490,6 +1492,50 @@
     }
 
     /**
+     * Contains information about desired display configuration.
+     *
+     * @hide
+     */
+    public static final class DesiredDisplayConfigSpecs {
+        /**
+         * @hide
+         */
+        public int mDefaultModeId;
+
+        /**
+         * @hide
+         */
+        public float mMinRefreshRate;
+
+        /**
+         * @hide
+         */
+        public float mMaxRefreshRate;
+
+        /**
+         * @hide
+         */
+        public DesiredDisplayConfigSpecs(
+                int defaultModeId, float minRefreshRate, float maxRefreshRate) {
+            mDefaultModeId = defaultModeId;
+            mMinRefreshRate = minRefreshRate;
+            mMaxRefreshRate = maxRefreshRate;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
+            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+
+        return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
+    }
+
+    /**
      * @hide
      */
     public static int[] getDisplayColorModes(IBinder displayToken) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 66bf982..f2f84cd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -529,6 +529,20 @@
     public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN =
             "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN";
 
+    /**
+     * Argument to represent the duration in milliseconds to press and hold a node.
+     * <p>
+     * <strong>Type:</strong> int<br>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_PRESS_AND_HOLD}</li>
+     * </ul>
+     *
+     * @see AccessibilityAction#ACTION_PRESS_AND_HOLD
+     */
+    public static final String ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT =
+            "android.view.accessibility.action.ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT";
+
     // Focus types
 
     /**
@@ -4035,6 +4049,8 @@
                 return "ACTION_SHOW_TOOLTIP";
             case R.id.accessibilityActionHideTooltip:
                 return "ACTION_HIDE_TOOLTIP";
+            case R.id.accessibilityActionPressAndHold:
+                return "ACTION_PRESS_AND_HOLD";
             default:
                 return "ACTION_UNKNOWN";
         }
@@ -4626,6 +4642,31 @@
         public static final AccessibilityAction ACTION_HIDE_TOOLTIP =
                 new AccessibilityAction(R.id.accessibilityActionHideTooltip);
 
+        /**
+         * Action that presses and holds a node.
+         * <p>
+         * This action is for nodes that have distinct behavior that depends on how long a press is
+         * held. Nodes having a single action for long press should use {@link #ACTION_LONG_CLICK}
+         *  instead of this action, and nodes should not expose both actions.
+         * <p>
+         * Use {@link #ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT} to specify how long the
+         * node is pressed. To ensure reasonable behavior, the first value of this argument should
+         * be 0 and the others should greater than 0 and less than 10,000. UIs requested to hold for
+         * times outside of this range should ignore the action.
+         * <p>
+         * The total time the element is held could be specified by an accessibility user up-front,
+         * or may depend on what happens on the UI as the user continues to request the hold.
+         * <p>
+         *   <strong>Note:</strong> The time between dispatching the action and it arriving in the
+         *     UI process is not guaranteed. It is possible on a busy system for the time to expire
+         *     unexpectedly. For the case of holding down a key for a repeating action, a delayed
+         *     arrival should be benign. Please do not use this sort of action in cases where such
+         *     delays will lead to unexpected UI behavior.
+         * <p>
+         */
+        @NonNull public static final AccessibilityAction ACTION_PRESS_AND_HOLD =
+                new AccessibilityAction(R.id.accessibilityActionPressAndHold);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2af7ac7..4b47927 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -125,7 +125,7 @@
          *
          * @param webview the WebView to transport
          */
-        public synchronized void setWebView(WebView webview) {
+        public synchronized void setWebView(@Nullable WebView webview) {
             mWebview = webview;
         }
 
@@ -134,6 +134,7 @@
          *
          * @return the transported WebView object
          */
+        @Nullable
         public synchronized WebView getWebView() {
             return mWebview;
         }
@@ -309,7 +310,7 @@
      *
      * @param context an Activity Context to access application assets
      */
-    public WebView(Context context) {
+    public WebView(@NonNull Context context) {
         this(context, null);
     }
 
@@ -319,7 +320,7 @@
      * @param context an Activity Context to access application assets
      * @param attrs an AttributeSet passed to our parent
      */
-    public WebView(Context context, AttributeSet attrs) {
+    public WebView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.webViewStyle);
     }
 
@@ -332,7 +333,7 @@
      *        reference to a style resource that supplies default values for
      *        the view. Can be 0 to not look for defaults.
      */
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
@@ -349,7 +350,8 @@
      *        defStyleAttr is 0 or can not be found in the theme. Can be 0
      *        to not look for defaults.
      */
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
         this(context, attrs, defStyleAttr, defStyleRes, null, false);
     }
 
@@ -370,7 +372,7 @@
      * and {@link WebStorage} for fine-grained control of privacy data.
      */
     @Deprecated
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr,
+    public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             boolean privateBrowsing) {
         this(context, attrs, defStyleAttr, 0, null, privateBrowsing);
     }
@@ -395,8 +397,8 @@
      *       be added synchronously, before a subsequent loadUrl call takes effect.
      */
     @UnsupportedAppUsage
-    protected WebView(Context context, AttributeSet attrs, int defStyleAttr,
-            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+    protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            @Nullable Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
         this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing);
     }
 
@@ -405,8 +407,9 @@
      */
     @SuppressWarnings("deprecation")  // for super() call into deprecated base class constructor.
     @UnsupportedAppUsage
-    protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
-            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+    protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes, @Nullable Map<String, Object> javaScriptInterfaces,
+            boolean privateBrowsing) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
         // WebView is important by default, unless app developer overrode attribute.
@@ -642,7 +645,7 @@
      *         method fails.
      */
     @Nullable
-    public WebBackForwardList saveState(Bundle outState) {
+    public WebBackForwardList saveState(@NonNull Bundle outState) {
         checkThread();
         return mProvider.saveState(outState);
     }
@@ -695,7 +698,7 @@
      * @return the restored back/forward list or {@code null} if restoreState failed
      */
     @Nullable
-    public WebBackForwardList restoreState(Bundle inState) {
+    public WebBackForwardList restoreState(@NonNull Bundle inState) {
         checkThread();
         return mProvider.restoreState(inState);
     }
@@ -713,7 +716,7 @@
      *            controlling caching, accept types or the User-Agent, their
      *            values may be overridden by this WebView's defaults.
      */
-    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+    public void loadUrl(@NonNull String url, @NonNull Map<String, String> additionalHttpHeaders) {
         checkThread();
         mProvider.loadUrl(url, additionalHttpHeaders);
     }
@@ -725,7 +728,7 @@
      *
      * @param url the URL of the resource to load
      */
-    public void loadUrl(String url) {
+    public void loadUrl(@NonNull String url) {
         checkThread();
         mProvider.loadUrl(url);
     }
@@ -739,7 +742,7 @@
      * @param postData the data will be passed to "POST" request, which must be
      *     be "application/x-www-form-urlencoded" encoded.
      */
-    public void postUrl(String url, byte[] postData) {
+    public void postUrl(@NonNull String url, @NonNull byte[] postData) {
         checkThread();
         if (URLUtil.isNetworkUrl(url)) {
             mProvider.postUrl(url, postData);
@@ -803,7 +806,8 @@
      * @param mimeType the MIME type of the data, e.g. 'text/html'.
      * @param encoding the encoding of the data
      */
-    public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
+    public void loadData(@NonNull String data, @Nullable String mimeType,
+            @Nullable String encoding) {
         checkThread();
         mProvider.loadData(data, mimeType, encoding);
     }
@@ -850,7 +854,7 @@
      * @param historyUrl the URL to use as the history entry. If {@code null} defaults
      *                   to 'about:blank'. If non-null, this must be a valid URL.
      */
-    public void loadDataWithBaseURL(@Nullable String baseUrl, String data,
+    public void loadDataWithBaseURL(@Nullable String baseUrl, @NonNull String data,
             @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) {
         checkThread();
         mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
@@ -873,7 +877,8 @@
      *                       completes with the result of the execution (if any).
      *                       May be {@code null} if no notification of the result is required.
      */
-    public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
+    public void evaluateJavascript(@NonNull String script, @Nullable ValueCallback<String>
+            resultCallback) {
         checkThread();
         mProvider.evaluateJavaScript(script, resultCallback);
     }
@@ -883,7 +888,7 @@
      *
      * @param filename the filename where the archive should be placed
      */
-    public void saveWebArchive(String filename) {
+    public void saveWebArchive(@NonNull String filename) {
         checkThread();
         mProvider.saveWebArchive(filename);
     }
@@ -900,8 +905,8 @@
      *                 under which the file was saved, or {@code null} if saving the
      *                 file failed.
      */
-    public void saveWebArchive(String basename, boolean autoname, @Nullable ValueCallback<String>
-            callback) {
+    public void saveWebArchive(@NonNull String basename, boolean autoname,
+            @Nullable ValueCallback<String> callback) {
         checkThread();
         mProvider.saveWebArchive(basename, autoname, callback);
     }
@@ -1064,7 +1069,7 @@
      *                  requests with callbacks.
      * @param callback  The callback to be invoked.
      */
-    public void postVisualStateCallback(long requestId, VisualStateCallback callback) {
+    public void postVisualStateCallback(long requestId, @NonNull VisualStateCallback callback) {
         checkThread();
         mProvider.insertVisualStateCallback(requestId, callback);
     }
@@ -1131,7 +1136,8 @@
      * @param documentName  The user-facing name of the printed document. See
      *                      {@link android.print.PrintDocumentInfo}
      */
-    public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
+    @NonNull
+    public PrintDocumentAdapter createPrintDocumentAdapter(@NonNull String documentName) {
         checkThread();
         return mProvider.createPrintDocumentAdapter(documentName);
     }
@@ -1203,6 +1209,7 @@
      * and the email is set in the "extra" field of HitTestResult. Otherwise,
      * HitTestResult type is set to UNKNOWN_TYPE.
      */
+    @NonNull
     public HitTestResult getHitTestResult() {
         checkThread();
         return mProvider.getHitTestResult();
@@ -1233,7 +1240,7 @@
      * @param msg the message to be dispatched with the result of the request
      *            as the data member with "url" as key. The result can be {@code null}.
      */
-    public void requestImageRef(Message msg) {
+    public void requestImageRef(@NonNull Message msg) {
         checkThread();
         mProvider.requestImageRef(msg);
     }
@@ -1243,10 +1250,11 @@
      * passed to WebViewClient.onPageStarted because although the load for
      * that URL has begun, the current page may not have changed.
      *
-     * @return the URL for the current page
+     * @return the URL for the current page or {@code null} if no page has been loaded
      */
     @InspectableProperty(hasAttributeId = false)
     @ViewDebug.ExportedProperty(category = "webview")
+    @Nullable
     public String getUrl() {
         checkThread();
         return mProvider.getUrl();
@@ -1259,10 +1267,12 @@
      * Also, there may have been redirects resulting in a different URL to that
      * originally requested.
      *
-     * @return the URL that was originally requested for the current page
+     * @return the URL that was originally requested for the current page or
+     * {@code null} if no page has been loaded
      */
     @InspectableProperty(hasAttributeId = false)
     @ViewDebug.ExportedProperty(category = "webview")
+    @Nullable
     public String getOriginalUrl() {
         checkThread();
         return mProvider.getOriginalUrl();
@@ -1272,10 +1282,11 @@
      * Gets the title for the current page. This is the title of the current page
      * until WebViewClient.onReceivedTitle is called.
      *
-     * @return the title for the current page
+     * @return the title for the current page or {@code null} if no page has been loaded
      */
     @InspectableProperty(hasAttributeId = false)
     @ViewDebug.ExportedProperty(category = "webview")
+    @Nullable
     public String getTitle() {
         checkThread();
         return mProvider.getTitle();
@@ -1285,9 +1296,11 @@
      * Gets the favicon for the current page. This is the favicon of the current
      * page until WebViewClient.onReceivedIcon is called.
      *
-     * @return the favicon for the current page
+     * @return the favicon for the current page or {@code null} if the page doesn't
+     * have one or if no page has been loaded
      */
     @InspectableProperty(hasAttributeId = false)
+    @Nullable
     public Bitmap getFavicon() {
         checkThread();
         return mProvider.getFavicon();
@@ -1523,6 +1536,7 @@
      * different objects. The object returned from this method will not be
      * updated to reflect any new state.
      */
+    @NonNull
     public WebBackForwardList copyBackForwardList() {
         checkThread();
         return mProvider.copyBackForwardList();
@@ -1535,7 +1549,7 @@
      *
      * @param listener an implementation of {@link FindListener}
      */
-    public void setFindListener(FindListener listener) {
+    public void setFindListener(@Nullable FindListener listener) {
         checkThread();
         setupFindListenerIfNeeded();
         mFindListener.mUserFindListener = listener;
@@ -1580,7 +1594,7 @@
      * @param find the string to find.
      * @see #setFindListener
      */
-    public void findAllAsync(String find) {
+    public void findAllAsync(@NonNull String find) {
         checkThread();
         mProvider.findAllAsync(find);
     }
@@ -1682,7 +1696,7 @@
      *
      * @param response the message that will be dispatched with the result
      */
-    public void documentHasImages(Message response) {
+    public void documentHasImages(@NonNull Message response) {
         checkThread();
         mProvider.documentHasImages(response);
     }
@@ -1694,7 +1708,7 @@
      * @param client an implementation of WebViewClient
      * @see #getWebViewClient
      */
-    public void setWebViewClient(WebViewClient client) {
+    public void setWebViewClient(@NonNull WebViewClient client) {
         checkThread();
         mProvider.setWebViewClient(client);
     }
@@ -1705,6 +1719,7 @@
      * @return the WebViewClient, or a default client if not yet set
      * @see #setWebViewClient
      */
+    @NonNull
     public WebViewClient getWebViewClient() {
         checkThread();
         return mProvider.getWebViewClient();
@@ -1798,7 +1813,7 @@
      *
      * @param listener an implementation of DownloadListener
      */
-    public void setDownloadListener(DownloadListener listener) {
+    public void setDownloadListener(@Nullable DownloadListener listener) {
         checkThread();
         mProvider.setDownloadListener(listener);
     }
@@ -1811,7 +1826,7 @@
      * @param client an implementation of WebChromeClient
      * @see #getWebChromeClient
      */
-    public void setWebChromeClient(WebChromeClient client) {
+    public void setWebChromeClient(@Nullable WebChromeClient client) {
         checkThread();
         mProvider.setWebChromeClient(client);
     }
@@ -1898,7 +1913,7 @@
      *               context. {@code null} values are ignored.
      * @param name the name used to expose the object in JavaScript
      */
-    public void addJavascriptInterface(Object object, String name) {
+    public void addJavascriptInterface(@NonNull Object object, @NonNull String name) {
         checkThread();
         mProvider.addJavascriptInterface(object, name);
     }
@@ -1926,6 +1941,7 @@
      *
      * @return the two message ports that form the message channel.
      */
+    @NonNull
     public WebMessagePort[] createWebMessageChannel() {
         checkThread();
         return mProvider.createWebMessageChannel();
@@ -1950,7 +1966,7 @@
      * @param message the WebMessage
      * @param targetOrigin the target origin.
      */
-    public void postWebMessage(WebMessage message, Uri targetOrigin) {
+    public void postWebMessage(@NonNull WebMessage message, @NonNull Uri targetOrigin) {
         checkThread();
         mProvider.postMessageToMainFrame(message, targetOrigin);
     }
@@ -1962,6 +1978,7 @@
      * @return a WebSettings object that can be used to control this WebView's
      *         settings
      */
+    @NonNull
     public WebSettings getSettings() {
         checkThread();
         return mProvider.getSettings();
@@ -2026,7 +2043,7 @@
      *                               in the current process.
      * @throws IllegalArgumentException if the suffix contains a path separator.
      */
-    public static void setDataDirectorySuffix(String suffix) {
+    public static void setDataDirectorySuffix(@NonNull String suffix) {
         WebViewFactory.setDataDirectorySuffix(suffix);
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 75a5804..25fa65e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -58,6 +58,7 @@
 import android.os.connectivity.GpsBatteryStats;
 import android.os.connectivity.WifiBatteryStats;
 import android.provider.Settings;
+import android.telephony.CellSignalStrength;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.ModemActivityInfo.TransmitPower;
@@ -832,7 +833,7 @@
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
     final StopwatchTimer[] mPhoneSignalStrengthsTimer =
-            new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+            new StopwatchTimer[CellSignalStrength.getNumSignalStrengthLevels()];
 
     StopwatchTimer mPhoneSignalScanningTimer;
 
@@ -1870,6 +1871,7 @@
             mCount = computeCurrentCountLocked();
             mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0;
             mUnpluggedReportedCount = mCurrentReportedCount = 0;
+            mTrackingReportedValues = false;
         }
 
         public void setUpdateVersion(int version) {
@@ -5130,7 +5132,7 @@
 
     void stopAllPhoneSignalStrengthTimersLocked(int except) {
         final long elapsedRealtime = mClocks.elapsedRealtime();
-        for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             if (i == except) {
                 continue;
             }
@@ -5145,7 +5147,7 @@
             // In this case we will always be STATE_OUT_OF_SERVICE, so need
             // to infer that we are scanning from other data.
             if (state == ServiceState.STATE_OUT_OF_SERVICE
-                    && signalBin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+                    && signalBin > CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 state = ServiceState.STATE_IN_SERVICE;
             }
         }
@@ -5168,7 +5170,7 @@
             // In this case we will always be STATE_OUT_OF_SERVICE, so need
             // to infer that we are scanning from other data.
             if (state == ServiceState.STATE_OUT_OF_SERVICE
-                    && strengthBin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+                    && strengthBin > CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 state = ServiceState.STATE_IN_SERVICE;
             }
         }
@@ -5185,7 +5187,7 @@
         // bin and have the scanning bit set.
         } else if (state == ServiceState.STATE_OUT_OF_SERVICE) {
             scanning = true;
-            strengthBin = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            strengthBin = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
             if (!mPhoneSignalScanningTimer.isRunningLocked()) {
                 mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
                 newHistory = true;
@@ -9785,7 +9787,7 @@
         mDeviceLightIdlingTimer = new StopwatchTimer(mClocks, null, -15, null, mOnBatteryTimeBase);
         mDeviceIdlingTimer = new StopwatchTimer(mClocks, null, -12, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(mClocks, null, -3, null, mOnBatteryTimeBase);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -200-i, null,
                     mOnBatteryTimeBase);
         }
@@ -10494,7 +10496,7 @@
         mFlashlightOnTimer.reset(false);
         mCameraOnTimer.reset(false);
         mBluetoothScanTimer.reset(false);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i].reset(false);
         }
         mPhoneSignalScanningTimer.reset(false);
@@ -10800,10 +10802,10 @@
                 mHasWifiReporting = true;
 
                 // Measured in mAms
-                final long txTimeMs = info.getControllerTxTimeMillis();
-                final long rxTimeMs = info.getControllerRxTimeMillis();
-                final long scanTimeMs = info.getControllerScanTimeMillis();
-                final long idleTimeMs = info.getControllerIdleTimeMillis();
+                final long txTimeMs = info.getControllerTxDurationMillis();
+                final long rxTimeMs = info.getControllerRxDurationMillis();
+                final long scanTimeMs = info.getControllerScanDurationMillis();
+                final long idleTimeMs = info.getControllerIdleDurationMillis();
                 final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
 
                 long leftOverRxTimeMs = rxTimeMs;
@@ -10946,13 +10948,14 @@
 
 
                 // Update WiFi controller stats.
-                mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
+                mWifiActivity.getRxTimeCounter().addCountLocked(
+                        info.getControllerRxDurationMillis());
                 mWifiActivity.getTxTimeCounters()[0].addCountLocked(
-                        info.getControllerTxTimeMillis());
+                        info.getControllerTxDurationMillis());
                 mWifiActivity.getScanTimeCounter().addCountLocked(
-                    info.getControllerScanTimeMillis());
+                        info.getControllerScanDurationMillis());
                 mWifiActivity.getIdleTimeCounter().addCountLocked(
-                        info.getControllerIdleTimeMillis());
+                        info.getControllerIdleDurationMillis());
 
                 // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
                 final double opVolt = mPowerProfile.getAveragePower(
@@ -10960,7 +10963,7 @@
                 if (opVolt != 0) {
                     // We store the power drain as mAms.
                     mWifiActivity.getPowerCounter().addCountLocked(
-                            (long) (info.getControllerEnergyUsed() / opVolt));
+                            (long) (info.getControllerEnergyUsedMicroJoules() / opVolt));
                 }
                 // Converting uWs to mAms.
                 // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
@@ -11055,7 +11058,7 @@
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
                     List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
                     for (int i = 0; i < Math.min(txPowerInfo.size(),
-                            SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+                            CellSignalStrength.getNumSignalStrengthLevels()); i++) {
                         energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
                             .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
                     }
@@ -12606,7 +12609,8 @@
         for (int i = 0; i < timeInRatMs.length; i++) {
            timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000;
         }
-        long[] timeInRxSignalStrengthLevelMs = new long[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+        long[] timeInRxSignalStrengthLevelMs =
+                new long[CellSignalStrength.getNumSignalStrengthLevels()];
         for (int i = 0; i < timeInRxSignalStrengthLevelMs.length; i++) {
            timeInRxSignalStrengthLevelMs[i]
                = getPhoneSignalStrengthTime(i, rawRealTime, which) / 1000;
@@ -13614,7 +13618,7 @@
         mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in);
         mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
         mPhoneOnTimer.readSummaryFromParcelLocked(in);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
         }
         mPhoneSignalScanningTimer.readSummaryFromParcelLocked(in);
@@ -14099,7 +14103,7 @@
         mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
         mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -14577,7 +14581,7 @@
                 mOnBatteryTimeBase, in);
         mDeviceIdlingTimer = new StopwatchTimer(mClocks, null, -12, null, mOnBatteryTimeBase, in);
         mPhoneOnTimer = new StopwatchTimer(mClocks, null, -3, null, mOnBatteryTimeBase, in);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -200-i,
                     null, mOnBatteryTimeBase, in);
         }
@@ -14796,7 +14800,7 @@
         mDeviceLightIdlingTimer.writeToParcel(out, uSecRealtime);
         mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
         mPhoneOnTimer.writeToParcel(out, uSecRealtime);
-        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
         }
         mPhoneSignalScanningTimer.writeToParcel(out, uSecRealtime);
@@ -14987,7 +14991,7 @@
             mDeviceIdlingTimer.logState(pr, "  ");
             pr.println("*** Phone timer:");
             mPhoneOnTimer.logState(pr, "  ");
-            for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+            for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
                 pr.println("*** Phone signal strength #" + i + ":");
                 mPhoneSignalStrengthsTimer[i].logState(pr, "  ");
             }
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 9e8f06d..7c77d28 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -16,14 +16,14 @@
 package com.android.internal.os;
 
 import android.os.BatteryStats;
-import android.telephony.SignalStrength;
+import android.telephony.CellSignalStrength;
 import android.util.Log;
 
 public class MobileRadioPowerCalculator extends PowerCalculator {
     private static final String TAG = "MobileRadioPowerController";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
     private final double mPowerRadioOn;
-    private final double[] mPowerBins = new double[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+    private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()];
     private final double mPowerScan;
     private BatteryStats mStats;
     private long mTotalAppMobileActiveMs = 0;
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 1afc67b..e3623c5 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -360,6 +360,28 @@
     }
 
     /**
+     * @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);
+    }
+
+    /**
      * @return the closest point in {@param points} to the given {@param x} and {@param y}.
      */
     private Point findClosestPoint(int x, int y, Point[] points) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 51f8fcc..148b0a2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,6 +110,7 @@
                 "android_view_InputEventReceiver.cpp",
                 "android_view_InputEventSender.cpp",
                 "android_view_InputQueue.cpp",
+                "android_view_FrameMetricsObserver.cpp",
                 "android_view_KeyCharacterMap.cpp",
                 "android_view_KeyEvent.cpp",
                 "android_view_MotionEvent.cpp",
@@ -424,9 +425,9 @@
         android: {
             srcs: [ // sources that depend on android only libraries
                 "android/graphics/apex/android_canvas.cpp",
+                "android/graphics/apex/renderthread.cpp",
                 "android/graphics/apex/jni_runtime.cpp",
 
-                "android_view_FrameMetricsObserver.cpp",
                 "android_view_TextureLayer.cpp",
                 "android_view_ThreadedRenderer.cpp",
                 "android/graphics/BitmapRegionDecoder.cpp",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 2dec4b3..984f93c 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -12,7 +12,6 @@
 #include "SkWebpEncoder.h"
 
 #include "android_os_Parcel.h"
-#include "android_util_Binder.h"
 #include "android_nio_utils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include <hwui/Paint.h>
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 6ffa72a..f18632d 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -25,18 +25,13 @@
 #include "SkBitmapRegionDecoder.h"
 #include "SkCodec.h"
 #include "SkData.h"
-#include "SkUtils.h"
-#include "SkPixelRef.h"
 #include "SkStream.h"
 
-#include "android_nio_utils.h"
-#include "android_util_Binder.h"
 #include "core_jni_helpers.h"
 
 #include <HardwareBitmapUploader.h>
 #include <nativehelper/JNIHelp.h>
 #include <androidfw/Asset.h>
-#include <binder/Parcel.h>
 #include <jni.h>
 #include <sys/stat.h>
 
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 7a9fea7..4de6c86 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -7,6 +7,7 @@
 #include "Utils.h"
 
 #include <nativehelper/JNIHelp.h>
+#include <log/log.h>
 #include <memory>
 
 static jmethodID    gInputStream_readMethodID;
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 1b27765..4814452 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -497,12 +497,12 @@
 
     static jint getFillType(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        return obj->getFillType();
+        return static_cast<int>(obj->getFillType());
     }
 
     static void setFillType(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle, jint ftHandle) {;
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPath::FillType ft = static_cast<SkPath::FillType>(ftHandle);
+        SkPathFillType ft = static_cast<SkPathFillType>(ftHandle);
         path->setFillType(ft);
     }
 };
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index bd28fe0..6095ffa 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -5,6 +5,7 @@
 #include "SkShader.h"
 #include "SkBlendMode.h"
 #include "core_jni_helpers.h"
+#include "src/shaders/SkRTShader.h"
 
 #include <jni.h>
 
@@ -212,6 +213,44 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
+        jbyteArray inputs, jlong colorSpaceHandle) {
+    SkRuntimeShaderFactory* factory = reinterpret_cast<SkRuntimeShaderFactory*>(shaderFactory);
+    AutoJavaByteArray arInputs(env, inputs);
+
+    sk_sp<SkData> fData;
+    fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    sk_sp<SkShader> shader = factory->make(fData, matrix);
+    ThrowIAE_IfNull(env, shader);
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl,
+        jboolean isOpaque) {
+    ScopedUtfChars strSksl(env, sksl);
+    SkRuntimeShaderFactory* shaderFactory = new SkRuntimeShaderFactory(SkString(strSksl.c_str()),
+            isOpaque == JNI_TRUE);
+    ThrowIAE_IfNull(env, shaderFactory);
+
+    return reinterpret_cast<jlong>(shaderFactory);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void RuntimeShader_delete(SkRuntimeShaderFactory* shaderFactory) {
+    delete shaderFactory;
+}
+
+static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RuntimeShader_delete));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
 static const JNINativeMethod gColorMethods[] = {
     { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
     { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
@@ -241,6 +280,13 @@
     { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
 };
 
+static const JNINativeMethod gRuntimeShaderMethods[] = {
+    { "nativeGetFinalizer",   "()J",    (void*)RuntimeShader_getNativeFinalizer },
+    { "nativeCreate",     "(JJ[BJ)J",  (void*)RuntimeShader_create     },
+    { "nativeCreateShaderFactory",     "(Ljava/lang/String;Z)J",
+      (void*)RuntimeShader_createShaderFactory     },
+};
+
 int register_android_graphics_Shader(JNIEnv* env)
 {
     android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
@@ -257,6 +303,8 @@
                                   NELEM(gSweepGradientMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
                                   NELEM(gComposeShaderMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
+                                  NELEM(gRuntimeShaderMethods));
 
     return 0;
 }
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 9603a10..4ce56ba 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -22,8 +22,6 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "SkTypeface.h"
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager.h>
 #include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
 #include <minikin/SystemFonts.h>
diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp
index 462d052..17c194d 100644
--- a/core/jni/android/graphics/Utils.cpp
+++ b/core/jni/android/graphics/Utils.cpp
@@ -18,6 +18,8 @@
 #include "SkUtils.h"
 #include "SkData.h"
 
+#include <log/log.h>
+
 using namespace android;
 
 AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset)
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
index ac291ea..8925517 100644
--- a/core/jni/android/graphics/Utils.h
+++ b/core/jni/android/graphics/Utils.h
@@ -19,8 +19,6 @@
 
 #include "SkStream.h"
 
-#include "android_util_Binder.h"
-
 #include <jni.h>
 #include <androidfw/Asset.h>
 
diff --git a/core/jni/android/graphics/apex/include/android/graphics/renderthread.h b/core/jni/android/graphics/apex/include/android/graphics/renderthread.h
new file mode 100644
index 0000000..0a790af
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/renderthread.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_GRAPHICS_RENDERTHREAD_H
+#define ANDROID_GRAPHICS_RENDERTHREAD_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Dumps a textual representation of the graphics stats for this process.
+ * @param fd The file descriptor that the available graphics stats will be appended to.  The
+ *           function requires a valid fd, but does not persist or assume ownership of the fd
+ *           outside the scope of this function.
+ */
+void ARenderThread_dumpGraphicsMemory(int fd);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_RENDERTHREAD_H
diff --git a/core/jni/android/graphics/apex/renderthread.cpp b/core/jni/android/graphics/apex/renderthread.cpp
new file mode 100644
index 0000000..5d26afe
--- /dev/null
+++ b/core/jni/android/graphics/apex/renderthread.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include "android/graphics/renderthread.h"
+
+#include <renderthread/RenderProxy.h>
+
+using namespace android;
+
+void ARenderThread_dumpGraphicsMemory(int fd) {
+    uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+}
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
index ca8b8de..5aa684d 100644
--- a/core/jni/android_app_ActivityThread.cpp
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -17,13 +17,11 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
-#include <minikin/Layout.h>
-#include <renderthread/RenderProxy.h>
-
 #include "core_jni_helpers.h"
 #include <unistd.h>
 
 #include <bionic/malloc.h>
+#include <android/graphics/renderthread.h>
 
 namespace android {
 
@@ -35,7 +33,7 @@
 static void
 android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
     int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
-    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+    ARenderThread_dumpGraphicsMemory(fd);
 }
 
 static void android_app_ActivityThread_initZygoteChildHeapProfiling(JNIEnv* env, jobject clazz) {
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index cb7f0dd..f2a51ad 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -396,9 +396,8 @@
 }
 
 static sp<ANativeWindow> getSurfaceTextureNativeWindow(JNIEnv* env, jobject thiz) {
-    sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
     sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
-    sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
+    sp<Surface> surfaceTextureClient(producer != NULL ? new Surface(producer) : NULL);
     return surfaceTextureClient;
 }
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2232393..0992beb 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -30,12 +30,13 @@
 #include <unistd.h>
 
 #include <android-base/stringprintf.h>
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-#include <binder/IPCThreadState.h>
-#include <binder/Parcel.h>
 #include <binder/BpBinder.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
 #include <binder/ProcessState.h>
+#include <binder/Stability.h>
 #include <cutils/atomic.h>
 #include <log/log.h>
 #include <utils/KeyedVector.h>
@@ -459,6 +460,9 @@
         sp<JavaBBinder> b = mBinder.promote();
         if (b == NULL) {
             b = new JavaBBinder(env, obj);
+            if (mVintf) {
+                ::android::internal::Stability::markVintf(b.get());
+            }
             mBinder = b;
             ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
                  b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
@@ -473,9 +477,18 @@
         return mBinder.promote();
     }
 
+    void markVintf() {
+        mVintf = true;
+    }
+
 private:
     Mutex           mLock;
     wp<JavaBBinder> mBinder;
+
+    // in the future, we might condense this into int32_t stability, or if there
+    // is too much binder state here, we can think about making JavaBBinder an
+    // sp here (avoid recreating it)
+    bool            mVintf = false;
 };
 
 // ----------------------------------------------------------------------------
@@ -965,6 +978,12 @@
     IPCThreadState::self()->restoreCallingWorkSource(token);
 }
 
+static void android_os_Binder_markVintfStability(JNIEnv* env, jobject clazz) {
+    JavaBBinderHolder* jbh =
+        (JavaBBinderHolder*) env->GetLongField(clazz, gBinderOffsets.mObject);
+    jbh->markVintf();
+}
+
 static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
 {
     IPCThreadState::self()->flushCommands();
@@ -1041,6 +1060,7 @@
     // @CriticalNative
     { "clearCallingWorkSource", "()J", (void*)android_os_Binder_clearCallingWorkSource },
     { "restoreCallingWorkSource", "(J)V", (void*)android_os_Binder_restoreCallingWorkSource },
+    { "markVintfStability", "()V", (void*)android_os_Binder_markVintfStability},
     { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
     { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
     { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
diff --git a/core/jni/android_util_StatsLog.cpp b/core/jni/android_util_StatsLog.cpp
index e749d34..9225fc2 100644
--- a/core/jni/android_util_StatsLog.cpp
+++ b/core/jni/android_util_StatsLog.cpp
@@ -18,18 +18,17 @@
 #define LOG_TAG "StatsLog_println"
 
 #include <assert.h>
-#include <cutils/properties.h>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "utils/misc.h"
 #include "core_jni_helpers.h"
-#include "stats_event_list.h"
+#include "stats_buffer_writer.h"
 
 namespace android {
 
-static void android_util_StatsLog_writeRaw(JNIEnv* env, jobject clazz, jbyteArray buf, jint size)
-{
+static void android_util_StatsLog_write(JNIEnv* env, jobject clazz, jbyteArray buf, jint size,
+        jint atomId) {
     if (buf == NULL) {
         return;
     }
@@ -42,13 +41,8 @@
     if (bufferArray == NULL) {
         return;
     }
-    const uint32_t statsEventTag = 1937006964;
-    struct iovec vec[2];
-    vec[0].iov_base = (void*) &statsEventTag;
-    vec[0].iov_len = sizeof(statsEventTag);
-    vec[1].iov_base = (void*) bufferArray;
-    vec[1].iov_len = size;
-    write_to_statsd(vec, 2);
+
+    write_buffer_to_statsd((void*) bufferArray, size, atomId);
 
     env->ReleaseByteArrayElements(buf, bufferArray, 0);
 }
@@ -58,7 +52,7 @@
  */
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "writeRaw", "([BI)V", (void*) android_util_StatsLog_writeRaw },
+    { "writeImpl", "([BII)V", (void*) android_util_StatsLog_write },
 };
 
 int register_android_util_StatsLog(JNIEnv* env)
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ba538a8..3531cf2 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -23,11 +23,10 @@
 #include <inttypes.h>
 
 #include <android_runtime/AndroidRuntime.h>
-#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/DisplayEventDispatcher.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
 #include <utils/threads.h>
-#include <gui/DisplayEventReceiver.h>
 #include "android_os_MessageQueue.h"
 
 #include <nativehelper/ScopedLocalRef.h>
@@ -59,7 +58,6 @@
 private:
     jobject mReceiverWeakGlobal;
     sp<MessageQueue> mMessageQueue;
-    DisplayEventReceiver mReceiver;
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9f20388..c6e678ab 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -138,6 +138,14 @@
     jmethodID builder;
 } gScreenshotGraphicBufferClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+    jfieldID defaultModeId;
+    jfieldID minRefreshRate;
+    jfieldID maxRefreshRate;
+} gDesiredDisplayConfigSpecsClassInfo;
+
 class JNamedColorSpace {
 public:
     // ColorSpace.Named.SRGB.ordinal() = 0;
@@ -810,6 +818,23 @@
     return allowedConfigsArray;
 }
 
+static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
+                                                   jobject desiredDisplayConfigSpecs) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == nullptr) return JNI_FALSE;
+
+    jint defaultModeId = env->GetIntField(desiredDisplayConfigSpecs,
+                                          gDesiredDisplayConfigSpecsClassInfo.defaultModeId);
+    jfloat minRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs,
+                                               gDesiredDisplayConfigSpecsClassInfo.minRefreshRate);
+    jfloat maxRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs,
+                                               gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate);
+
+    size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(
+            token, defaultModeId, minRefreshRate, maxRefreshRate);
+    return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
 static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return -1;
@@ -1366,6 +1391,9 @@
             (void*)nativeSetAllowedDisplayConfigs },
     {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetAllowedDisplayConfigs },
+    {"nativeSetDesiredDisplayConfigSpecs",
+            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
+            (void*)nativeSetDesiredDisplayConfigSpecs },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
     {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1535,6 +1563,19 @@
     gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
             "Landroid/view/SurfaceControl$CieXyz;");
 
+    jclass desiredDisplayConfigSpecsClazz =
+            FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs");
+    gDesiredDisplayConfigSpecsClassInfo.clazz =
+            MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
+    gDesiredDisplayConfigSpecsClassInfo.ctor =
+            GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFF)V");
+    gDesiredDisplayConfigSpecsClassInfo.defaultModeId =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mDefaultModeId", "I");
+    gDesiredDisplayConfigSpecsClassInfo.minRefreshRate =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mMinRefreshRate", "F");
+    gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mMaxRefreshRate", "F");
+
     return err;
 }
 
diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp
index 6475151..5491a33 100644
--- a/core/jni/android_view_TextureLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include "jni.h"
-#include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
 
 #include "core_jni_helpers.h"
@@ -27,12 +26,8 @@
 #include <gui/surfacetexture/surface_texture_platform.h>
 #include <gui/surfacetexture/SurfaceTexture.h>
 #include <hwui/Paint.h>
-
 #include <SkMatrix.h>
-
 #include <DeferredLayerUpdater.h>
-#include <Rect.h>
-#include <RenderNode.h>
 
 namespace android {
 
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 8fabb23..fd6984b 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -36,6 +36,7 @@
   "/apex/com.android.conscrypt/javalib/conscrypt.jar",
   "/apex/com.android.ipsec/javalib/ike.jar",
   "/apex/com.android.media/javalib/updatable-media.jar",
+  "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
   "/dev/null",
   "/dev/socket/zygote",
   "/dev/socket/zygote_secondary",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 936099f..9dc2319 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1614,6 +1614,14 @@
     <permission android:name="android.permission.NETWORK_STACK"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows an application to observe network policy changes. -->
+    <permission android:name="android.permission.OBSERVE_NETWORK_POLICY"
+                android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Allows applications to register network factory or agent -->
+    <permission android:name="android.permission.NETWORK_FACTORY"
+                android:protectionLevel="signature" />
+
     <!-- Allows Settings and SystemUI to call methods in Networking services
          <p>Not for use by third-party or privileged applications.
          @SystemApi
@@ -1673,13 +1681,13 @@
     <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
         android:protectionLevel="signature" />
 
-    <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
-         when the device is stationary in order to save power.
+    <!-- @SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can
+         be increased when the device is stationary in order to save power.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
         android:protectionLevel="signature|privileged" />
 
-    <!-- #SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
+    <!-- @SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
         android:protectionLevel="signature|privileged" />
@@ -1744,7 +1752,11 @@
     <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
         android:protectionLevel="normal" />
 
-    <!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
+    <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
+         uses have been replaced by a more appropriate permission. Most uses have been replaced with
+         a NETWORK_STACK or NETWORK_SETTINGS check. Please look up the documentation of the
+         individual functions to figure out what permission now protects the individual function.
+         @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
          @hide -->
     <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
         android:protectionLevel="signature|privileged" />
@@ -3536,9 +3548,10 @@
         android:protectionLevel="signature|privileged" />
 
     <!-- @SystemApi Allows an application to manage the holders of a role.
-         @hide -->
+         @hide
+         STOPSHIP b/145526313: Remove wellbeing protection flag from MANAGE_ROLE_HOLDERS. -->
     <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
-                android:protectionLevel="signature|installer|telephony" />
+                android:protectionLevel="signature|installer|telephony|wellbeing" />
 
     <!-- @SystemApi Allows an application to observe role holder changes.
          @hide -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index d095690..4acdeeb 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Dien in"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bestuurprogram werk tans"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tik om die bestuurprogram te verlaat."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Verbinding of Wi-Fi-warmkol aktief"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tik om op te stel."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Verbinding is gedeaktiveer"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontak jou administrateur vir besonderhede"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Terug"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Volgende"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Slaan oor"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 75444ea..c6e55c3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"አስረክብ"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"የመንዳት መተግበሪያ እያሄደ ነው"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ከመንዳት መተግበሪያ ለመውጣት መታ ያድርጉ።"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ለማዋቀር መታ ያድርጉ።"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ተመለስ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ቀጥሎ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ዝለል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 9928436..609b77e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1509,10 +1509,6 @@
     <string name="submit" msgid="862795280643405865">"إرسال"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"تطبيق القيادة قيد التشغيل"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"انقر للخروج من تطبيق القيادة."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"النطاق أو نقطة الاتصال نشطة"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"انقر للإعداد."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"تم إيقاف التوصيل"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"اتصل بالمشرف للحصول على التفاصيل"</string>
     <string name="back_button_label" msgid="4078224038025043387">"رجوع"</string>
     <string name="next_button_label" msgid="6040209156399907780">"التالي"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"التخطي"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index cfd6239..091b563 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"দাখিল কৰক"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ড্ৰাইভিং এপ্ চলি আছে"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ড্ৰাইভিং এপৰ পৰা বাহিৰ হ\'বলৈ টিপক।"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ছেট আপ কৰিবলৈ টিপক।"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
     <string name="back_button_label" msgid="4078224038025043387">"উভতি যাওক"</string>
     <string name="next_button_label" msgid="6040209156399907780">"পৰৱৰ্তী"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"এৰি যাওক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a26d49d..359dfb7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Göndər"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sürücülük tətbiqi işləyir"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sürücülük tətbiqindən çıxmaq üçün klikləyin."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tezerinq və ya hotspot aktivdir"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Quraşdırmaq üçün tıklayın."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Birləşmə deaktivdir"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Məlumat üçün adminlə əlaqə saxlayın"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Geri"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Növbəti"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Keç"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 6d0ae6b..a27d359 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1449,10 +1449,6 @@
     <string name="submit" msgid="862795280643405865">"Pošalji"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnju je pokrenuta"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite da biste izašli iz aplikacije za vožnju."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite da biste podesili."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Privezivanje je onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Potražite detalje od administratora"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Nazad"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 390e8b1..fb0fa33 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Перадаць"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Праграма для ваджэння ўключана"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Націсніце, каб выйсці з праграмы для ваджэння."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Дакраніцеся, каб наладзіць."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Рэжым мадэма адключаны"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Далей"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Прапусціць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d47229df..69c998d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Изпращане"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Приложението за шофиране е включено"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Докоснете, за да излезете от приложението за шофиране."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Има активна споделена връзка или безжична точка за достъп"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Докоснете, за да настроите."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Функцията за тетъринг е деактивирана"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Свържете се с администратора си за подробности"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Напред"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Пропускане"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index a767a71..e335396 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"জমা দিন"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ড্রাইভিং অ্যাপ চালু আছে"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ড্রাইভিং অ্যাপ বন্ধ করতে ট্যাপ করুন।"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"টিথারিং বা হটস্পট সক্রিয় আছে"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"সেট-আপ করার জন্য আলতো চাপুন৷"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"টিথারিং অক্ষম করা আছে"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ফিরুন"</string>
     <string name="next_button_label" msgid="6040209156399907780">"পরবর্তী"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"এড়িয়ে যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f1c1500..d7bd7a1 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1451,10 +1451,6 @@
     <string name="submit" msgid="862795280643405865">"Potvrdi"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnju je pokrenuta"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite za izlaz iz aplikacije za vožnju."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite za postavke"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Povezivanje putem mobitela je onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontaktirajte svog administratora za dodatne detalje"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Nazad"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Naprijed"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4b19405..1f7d82e 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Envia"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"S\'està executant l\'aplicació de conducció"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca per sortir de l\'aplicació de conducció."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Toca per configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"La compartició de xarxa està desactivada"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacta amb el teu administrador per obtenir més informació"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Enrere"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Següent"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Omet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 22fd4c6..e05f299 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Odeslat"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Jízdní aplikace je spuštěna"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Jízdní aplikaci zavřete klepnutím."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Sdílené připojení nebo hotspot je aktivní."</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Klepnutím zahájíte nastavení."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering je zakázán"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"O podrobnosti požádejte administrátora"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Zpět"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Další"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Přeskočit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4475a1b..772b7b0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Send"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bilkørselsappen er aktiv"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tryk for at lukke bilkørselsappen."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Netdeling eller hotspot er aktivt"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tryk for at konfigurere"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Netdeling er deaktiveret"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakt din administrator for at få oplysninger"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Tilbage"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Næste"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Spring over"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2489fca..bb29a0e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Senden"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Fahr-App wird ausgeführt"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tippen, um die Fahr-App zu beenden."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering oder Hotspot aktiv"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Zum Einrichten tippen."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering ist deaktiviert"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Bitte wende dich für weitere Informationen an den Administrator"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Zurück"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Weiter"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Überspringen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6c76a1f..807c4c2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Υποβολή"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Η εφαρμογή οδήγησης εκτελείται"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Πατήστε για να εξέλθετε από την εφαρμογή οδήγησης."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Πατήστε για ρύθμιση."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Η σύνδεση είναι απενεργοποιημένη"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Πίσω"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Επόμενο"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Παράλειψη"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Submit"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Back"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2cff16f..e538587 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Submit"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Back"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Submit"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Back"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Submit"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Back"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6d536e4..1436692 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎Submit‎‏‎‎‏‎"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‎Driving app is running‎‏‎‎‏‎"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎Tap to exit driving app.‎‏‎‎‏‎"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎Tap to set up.‎‏‎‎‏‎"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎Tethering is disabled‎‏‎‎‏‎"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎Contact your admin for details‎‏‎‎‏‎"</string>
     <string name="back_button_label" msgid="4078224038025043387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎Back‎‏‎‎‏‎"</string>
     <string name="next_button_label" msgid="6040209156399907780">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎Next‎‏‎‎‏‎"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎Skip‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3b9c7ad..b04882a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Se está ejecutando la app de conducción"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Presiona para salir de la app de conducción."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Anclaje a red o zona activa conectados"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Presiona para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Para obtener más información, comunícate con el administrador"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Atrás"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Siguiente"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2e74904..81f210b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicación de conducción en uso"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca para salir de la aplicación de conducción."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Compartir conexión/Zona Wi-Fi activada"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Toca para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"La conexión compartida está inhabilitada"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Ponte en contacto con el administrador para obtener más información"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Atrás"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Siguiente"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Saltar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8561254..61b18bb 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Saada"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sõidurakendus töötab"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Puudutage sõidurakendusest väljumiseks."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Jagamine või kuumkoht on aktiivne"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Puudutage seadistamiseks."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Jagamine on keelatud"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Tagasi"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Järgmine"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Jäta vahele"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 263b5ed..fda8565 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Bidali"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Abian da gidatzeko aplikazioa"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sakatu gidatzeko aplikaziotik irteteko."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Konexioa partekatzea edo sare publikoa aktibo"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Sakatu konfiguratzeko."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Desgaituta dago konexioa partekatzeko aukera"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Atzera"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Hurrengoa"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Saltatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 997b6af..433d4f3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ارسال"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"برنامه رانندگی درحال اجرا است"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"برای خروج از برنامه رانندگی ضربه بزنید."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"برای راه‌اندازی ضربه بزنید."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
     <string name="back_button_label" msgid="4078224038025043387">"برگشت"</string>
     <string name="next_button_label" msgid="6040209156399907780">"بعدی"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"رد شدن"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 86badf6..0c7bd77 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Lähetä"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Ajosovellus on käynnissä"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Poistu ajosovelluksesta napauttamalla."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Internetin jakaminen tai yhteyspiste käytössä"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Määritä napauttamalla."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Yhteyden jakaminen poistettu käytöstä"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kysy lisätietoja järjestelmänvalvojalta."</string>
     <string name="back_button_label" msgid="4078224038025043387">"Takaisin"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Seuraava"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ohita"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2b89065..3c8f75f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Envoyer"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"L\'application de conduite est en cours d\'exécution"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Touchez pour quitter l\'application de conduite."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Partage de connexion ou point d\'accès sans fil activé"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Touchez pour configurer."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Le partage de connexion est désactivé"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Précédent"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Suivante"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Passer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7522b5aa..1b53bd9 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Envoyer"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"L\'application de conduite est en cours d\'exécution"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Appuyez ici pour quitter l\'application de conduite."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Partage de connexion ou point d\'accès sans fil activé"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Appuyez ici pour configurer."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Le partage de connexion est désactivé"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Pour en savoir plus, contactez votre administrateur"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Retour"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Suivant"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 4d40e21..4572db7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Estase executando a aplicación de condución"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca para saír da aplicación de condución."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Conexión compartida ou zona wifi activada"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tocar para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"A conexión compartida está desactivada"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacta co administrador para obter información"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Volver"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Seguinte"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ea3a573..5e4f753 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"સબમિટ કરો"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ડ્રાઇવિંગ ઍપ ચાલી રહી છે"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ડ્રાઇવિંગ ઍપથી બહાર નીકળવા માટે ટૅપ કરો."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"સેટ કરવા માટે ટૅપ કરો."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ટિથરિંગ અક્ષમ કરેલ છે"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
     <string name="back_button_label" msgid="4078224038025043387">"પાછળ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"આગલું"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"છોડો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e12fa08..eecfd5b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"सबमिट करें"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्राइविंग ऐप्लिकेशन चल रहा है"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्राइविंग ऐप्लिकेशन से निकलने के लिए टैप करें."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"टेदरिंग या हॉटस्‍पॉट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"सेट करने के लिए टैप करें."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिंग अक्षम है"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
     <string name="back_button_label" msgid="4078224038025043387">"वापस जाएं"</string>
     <string name="next_button_label" msgid="6040209156399907780">"आगे बढ़ें"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"अभी नहीं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0871d06..e8f02b2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1449,10 +1449,6 @@
     <string name="submit" msgid="862795280643405865">"Pošalji"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Pokrenuta je aplikacija za vožnju"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite za zatvaranje aplikacije za vožnju."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Ograničenje ili aktivan hotspot"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite da biste postavili."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Modemsko je povezivanje onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Obratite se administratoru da biste saznali pojedinosti"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Natrag"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Dalje"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ce11fc6..1d1b994 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Elküldés"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Az autós alkalmazás fut"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Koppintson ide az autós alkalmazásból való kilépéshez."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Megosztás vagy aktív hotspot"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Koppintson a beállításhoz."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Az internetmegosztás le van tiltva"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"A részletekért forduljon rendszergazdájához"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Vissza"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Tovább"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Kihagyás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 629b225..599ce86 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Ուղարկել"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Հավելվածն աշխատում է"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Հպեք՝ հավելվածը փակելու համար:"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Մոդեմի ռեժիմը միացված է"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Հպեք՝ կարգավորելու համար:"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Մոդեմի ռեժիմն անջատված է"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Հետ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Հաջորդը"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Բաց թողնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f99c107..1f81eb5 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Kirim"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikasi mengemudi sedang berjalan"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketuk untuk keluar dari aplikasi mengemudi."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering (Penambatan) atau hotspot aktif"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Ketuk untuk menyiapkan."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering dinonaktifkan"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Hubungi admin untuk mengetahui detailnya"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Kembali"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Selanjutnya"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Lewati"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0f4d6de..d23cf5f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Senda"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Akstursforrit er í gangi"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ýttu til að loka akstursforritinu."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Kveikt á tjóðrun eða aðgangsstað"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Ýttu til að setja upp."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Slökkt er á tjóðrun"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Til baka"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Áfram"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Sleppa"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f14e858..8ebcb0e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Invia"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"App di guida in esecuzione"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tocca per uscire dall\'app di guida."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering oppure hotspot attivo"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tocca per impostare."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering disattivato"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Indietro"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Avanti"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ignora"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 375d7d1..85d0f3b 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"שלח"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"אפליקציית הנהיגה פועלת"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"יש להקיש כדי לצאת מאפליקציית הנהיגה."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"שיתוף אינטרנט פעיל"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"הקש כדי להגדיר."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"שיתוף האינטרנט בין ניידים מושבת"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"לפרטים, יש לפנות למנהל המערכת"</string>
     <string name="back_button_label" msgid="4078224038025043387">"הקודם"</string>
     <string name="next_button_label" msgid="6040209156399907780">"הבא"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"דילוג"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eb990ec..e21035f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"送信"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"運転アプリを実行しています"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"運転アプリを終了するにはタップしてください。"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"テザリングまたはアクセスポイントが有効です"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"タップしてセットアップします。"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"テザリングは無効に設定されています"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"詳しくは、管理者にお問い合わせください"</string>
     <string name="back_button_label" msgid="4078224038025043387">"戻る"</string>
     <string name="next_button_label" msgid="6040209156399907780">"次へ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"スキップ"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index d2d1fed..941dbbd 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"გაგზავნა"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"მართვის აპი გაშვებულია"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"შეეხეთ მართვის აპიდან გასასვლელად."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"შეეხეთ დასაყენებლად."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ტეტერინგი გათიშულია"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
     <string name="back_button_label" msgid="4078224038025043387">"უკან"</string>
     <string name="next_button_label" msgid="6040209156399907780">"მომდევნო"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"გამოტოვება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index a1002f7..b142e58 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Жіберу"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Көлік жүргізу қолданбасы қосулы"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Көлік жүргізу қолданбасынан шығу үшін түртіңіз."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Тетеринг немесе хотспот қосулы"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Реттеу үшін түртіңіз."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Тетеринг өшірілді"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Мәліметтерді әкімшіден алыңыз"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Артқа"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Келесі"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Өткізіп жіберу"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 61a3ec6..43289eb 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1431,10 +1431,6 @@
     <string name="submit" msgid="862795280643405865">"ដាក់​ស្នើ"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"កម្មវិធី​បើកបរ​កំពុង​ដំណើរការ"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ចុច​ដើម្បី​ចាកចេញ​ពីកម្មវិធី​បើកបរ។"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ប៉ះដើម្បីកំណត់"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ការភ្ជាប់​ត្រូវបានបិទ"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ថយក្រោយ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"បន្ទាប់​"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"រំលង"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5524d40..6ebe7b4 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ಸಲ್ಲಿಸು"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ಡ್ರೈವಿಂಗ್‌ ಅಪ್ಲಿಕೇಶನ್ ಚಾಲನೆಯಲ್ಲಿದೆ"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ಡ್ರೈವಿಂಗ್‌ ಅಪ್ಲಿಕೇಶನ್ ನಿರ್ಗಮಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ಹಿಂದೆ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ಮುಂದಿನದು"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ಸ್ಕಿಪ್‌"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 423dbd6..c6caedf 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"제출"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"운전 앱 실행 중"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"운전 앱을 종료하려면 탭하세요."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"테더링 또는 핫스팟 사용"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"설정하려면 탭하세요."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"테더링이 사용 중지됨"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"자세한 정보는 관리자에게 문의하세요."</string>
     <string name="back_button_label" msgid="4078224038025043387">"뒤로"</string>
     <string name="next_button_label" msgid="6040209156399907780">"다음"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"건너뛰기"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index d1d773c..341c996 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Тапшыруу"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Унаа айдоо колдонмосу иштеп жатат"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Унаа айдоо колдонмосунан чыгуу үчүн таптаңыз."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Жалгаштыруу же хотспот жандырылган"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Жөндөө үчүн таптап коюңуз."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Жалгаштыруу функциясы өчүрүлгөн"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Артка"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Кийинки"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Өткөрүп жиберүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ded2a4f..e0da206 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ສົ່ງຂໍ້ມູນ"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ແອັບຂັບລົດກຳລັງເຮັດວຽກຢູ່"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ແຕະເພື່ອອອກຈາກແອັບຂັບລົດ."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ກັບຄືນ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ຕໍ່ໄປ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ຂ້າມ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index eac535c..1212aa4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Pateikti"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Veikia vairavimo programa"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Palieskite, kad išeitumėte iš vairavimo programos."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Susietas ar aktyvus"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Palieskite, kad nustatytumėte."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Įrenginio kaip modemo naudojimas išjungtas"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Atgal"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Kitas"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Praleisti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f586212..547b6c9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1449,10 +1449,6 @@
     <string name="submit" msgid="862795280643405865">"Iesniegt"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Autovadīšanas lietotne darbojas"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Pieskarieties, lai izietu no autovadīšanas lietotnes"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Piesaiste vai tīklājs ir aktīvs."</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Pieskarieties, lai iestatītu."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Piesaiste ir atspējota"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
     <string name="back_button_label" msgid="4078224038025043387">"Atpakaļ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Tālāk"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Izlaist"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0d308a1..93f2da1 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Поднеси"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Апликацијата за возење работи"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Допрете за да излезете од апликацијата за возење."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Поврзувањето или точката на пристап се активни"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Допрете за поставување."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Врзувањето е оневозможено"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Контактирајте со администраторот за детали"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Следно"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Прескокни"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 63a7194..c220cdd 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"സമർപ്പിക്കുക"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ഡ്രൈവിംഗ് ആപ്പ് റൺ ചെയ്യുകയാണ്"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ഡ്രൈവിംഗ് ആപ്പിൽ നിന്ന് പുറത്തുകടക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
     <string name="back_button_label" msgid="4078224038025043387">"മടങ്ങുക"</string>
     <string name="next_button_label" msgid="6040209156399907780">"അടുത്തത്"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ഒഴിവാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b3cf5ce..8c26113 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Илгээх"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Жолоо барих апп ажиллаж байна"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Жолооны аппаас гарахын тулд товшино уу."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Модем болгох эсвэл идэвхтэй цэг болгох"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Тохируулахын тулд товшино уу."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Модем болгох боломжгүй байна"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Буцах"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Дараах"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Алгасах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 22b1dcc..2782caf 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"सबमिट करा"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्रायव्हिंग अ‍ॅप चालू आहे"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्रायव्हिंग ॲपमधून बाहेर पाडण्यासाठी टॅप करा."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"सेट करण्यासाठी टॅप करा."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिंग बंद आहे"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string>
     <string name="back_button_label" msgid="4078224038025043387">"मागे"</string>
     <string name="next_button_label" msgid="6040209156399907780">"पुढील"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"वगळा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 88c12b6..a40eca0 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Serah"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Apl memandu sedang berjalan"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketik untuk keluar daripada apl memandu."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Penambatan atau titik panas aktif"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Ketik untuk membuat persediaan."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Penambatan dilumpuhkan"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Hubungi pentadbir anda untuk maklumat lanjut"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Kembali"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Seterusnya"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Langkau"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index cdf68cd..64e2376 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ပေးပို့ရန်"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ကားမောင်းသည့်အက်ပ် ပွင့်နေပါသည်"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ကားမောင်းသည့်အက်ပ်မှ ထွက်ရန် တို့ပါ။"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"နောက်သို့"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ရှေ့သို့"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ကျော်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b129c3c..69e341e 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Send inn"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Kjøreappen kjører"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Trykk for å lukke kjøreappen."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Internettdeling eller trådløs sone er aktiv"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Trykk for å konfigurere."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Internettdeling er slått av"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Ta kontakt med administratoren din for å få mer informasjon"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Tilbake"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Neste"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Hopp over"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 648f47c..0fb2213 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1435,10 +1435,6 @@
     <string name="submit" msgid="862795280643405865">"पेस गर्नुहोस्"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्राइभिङ अनुप्रयोग चलिरहेको छ"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्राइभिङ अनुप्रयोगबाट बाहिर निस्कन ट्याप गर्नुहोस्।"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिङलाई असक्षम पारिएको छ"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
     <string name="back_button_label" msgid="4078224038025043387">"पछाडि"</string>
     <string name="next_button_label" msgid="6040209156399907780">"अर्को"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"छोड्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e248ca1..363c3c3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Verzenden"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Auto-app wordt uitgevoerd"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tik om de auto-app te sluiten."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering of hotspot actief"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tik om in te stellen."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is uitgeschakeld"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Neem contact op met je beheerder voor meer informatie"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Vorige"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Volgende"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Overslaan"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 7a73cc6..566d778 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ଦାଖଲ କରନ୍ତୁ"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ଡ୍ରାଇଭିଙ୍ଗ ଆପ୍ ଚାଲୁଛି"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ଡ୍ରାଇଭିଙ୍ଗ ଆପ୍‌ରୁ ବାହାରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ଫେରନ୍ତୁ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ପରବର୍ତ୍ତୀ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ଛାଡ଼ିଦିଅନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 3a245e3..7760db0 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ਪ੍ਰਸਤੁਤ ਕਰੋ"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ਗੱਡੀ ਚਲਾਉਣ ਸੰਬੰਧੀ ਐਪ ਚੱਲ ਰਹੀ ਹੈ"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ਗੱਡੀ ਚਲਾਉਣ ਸੰਬੰਧੀ ਐਪ ਤੋਂ ਬਾਹਰ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ਪਿੱਛੇ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ਅੱਗੇ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ਛੱਡੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7a86bd5..4ff7cc4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Prześlij"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Tryb samochodowy jest włączony"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Kliknij, by zakończyć tryb samochodowy."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Aktywny tethering lub punkt dostępu"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Kliknij, by skonfigurować."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering został wyłączony"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Wróć"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Dalej"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Pomiń"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 3f82a80..1750d2a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"O app para carro está sendo usado"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair do app para carro."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Ponto de acesso ou tethering ativo"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering desativado"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Fale com seu administrador para saber detalhes"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Voltar"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Avançar"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f67c96f..5c21c0d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"A aplicação de condução está em execução."</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair da aplicação de condução."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Ligação ponto a ponto ou hotspot activos"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"A ligação (à Internet) via telemóvel está desativada."</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacte o gestor para obter detalhes."</string>
     <string name="back_button_label" msgid="4078224038025043387">"Anterior"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Seguinte"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3f82a80..1750d2a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"O app para carro está sendo usado"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair do app para carro."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Ponto de acesso ou tethering ativo"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering desativado"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Fale com seu administrador para saber detalhes"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Voltar"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Avançar"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 846b982..397525a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1449,10 +1449,6 @@
     <string name="submit" msgid="862795280643405865">"Trimiteți"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicația pentru condus rulează"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atingeți ca să ieșiți din aplicația pentru condus."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering sau hotspot activ"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Atingeți ca să configurați."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tetheringul este dezactivat"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Contactați administratorul pentru detalii"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Înapoi"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Înainte"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Omiteți"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 19fde9f..a7abbff 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Отправить"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Приложение для вождения включено"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Нажмите, чтобы выйти из приложения для вождения."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Включен режим модема"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Нажмите, чтобы настроить."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Включить режим модема нельзя"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Обратитесь к администратору, чтобы узнать подробности."</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Далее"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Пропустить"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 676b48f..7d3f80d 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1431,10 +1431,6 @@
     <string name="submit" msgid="862795280643405865">"යොමු කරන්න"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"රියදුරු යෙදුම ධාවනය වේ."</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"රියදුරු යෙදුමෙන් පිටවීම සඳහා තට්ටු කරන්න."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"පිහිටුවීමට තට්ටු කරන්න."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ටෙදරින් අබල කර ඇත"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
     <string name="back_button_label" msgid="4078224038025043387">"ආපසු"</string>
     <string name="next_button_label" msgid="6040209156399907780">"මීලඟ"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"මඟ හරින්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cde5466..3a81468c 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Odoslať"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikácia na šoférovanie je spustená"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Klepnutím ukončíte aplikáciu na šoférovanie"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering alebo prístupový bod je aktívny"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Klepnutím prejdete na nastavenie."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering je deaktivovaný"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"O podrobnosti požiadajte svojho správcu"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Späť"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Ďalej"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Preskočiť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0494068..c28384c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Pošlji"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnjo se izvaja"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dotaknite se, če želite zapreti aplikacijo za vožnjo."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Dotaknite se, če želite nastaviti."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Za podrobnosti se obrnite na skrbnika"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Nazaj"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Naprej"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 20e6ae5..39f7377 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Dërgo"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacioni i drejtimit të makinës është në ekzekutim"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Trokit për të dalë nga aplikacioni i drejtimit të makinës."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Trokit për ta konfiguruar."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Lidhja e çiftimit është çaktivizuar"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakto me administratorin për detaje"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Prapa"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Përpara"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Kapërce"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index db53b23..e93c852 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1449,10 +1449,6 @@
     <string name="submit" msgid="862795280643405865">"Пошаљи"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Апликација за вожњу је покренута"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Додирните да бисте изашли из апликације за вожњу."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Додирните да бисте подесили."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Привезивање је онемогућено"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Потражите детаље од администратора"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Next"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Прескочи"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 9e99aec..cf0e0bb 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Skicka"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bilkörningsappen körs"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tryck här om du vill avsluta bilkörningsappen."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Internetdelning eller surfzon aktiverad"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Tryck om du vill konfigurera."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Internetdelning har inaktiverats"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakta administratören om du vill veta mer"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Tillbaka"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Nästa"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Hoppa över"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d7b3555..f8cd19c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Wasilisha"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Umewasha programu ya kuendesha gari"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Gusa ili ufunge programu ya kuendesha gari."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Kushiriki au kusambaza intaneti kumewashwa"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Gusa ili uweke mipangilio."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Umezima kipengele cha kusambaza mtandao"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Nyuma"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Endelea"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ruka"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 569977c..ac284d3 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"சமர்ப்பி"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"\'வாகனம் ஓட்டும் பயன்முறை’ ஆனில் உள்ளது"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"வாகனம் ஓட்டும் பயன்முறையிலிருந்து வெளியேற, தட்டவும்."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"அமைக்க, தட்டவும்."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
     <string name="back_button_label" msgid="4078224038025043387">"முந்தையது"</string>
     <string name="next_button_label" msgid="6040209156399907780">"அடுத்து"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"தவிர்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9600172..6641c41 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"సమర్పించు"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"డ్రైవింగ్ యాప్ అమలవుతోంది"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"డ్రైవింగ్ యాప్ నుండి నిష్క్రమించడం కోసం నొక్కండి."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"సెటప్ చేయడానికి నొక్కండి."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"టెథెరింగ్ నిలిపివేయబడింది"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string>
     <string name="back_button_label" msgid="4078224038025043387">"వెనుకకు"</string>
     <string name="next_button_label" msgid="6040209156399907780">"తర్వాత"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"దాటవేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4dd0d0f..bc4b79e 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"ส่ง"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"แอปสำหรับการขับขี่ทำงานอยู่"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"แตะเพื่อออกจากแอปสำหรับการขับขี่"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"แตะเพื่อตั้งค่า"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
     <string name="back_button_label" msgid="4078224038025043387">"กลับ"</string>
     <string name="next_button_label" msgid="6040209156399907780">"ถัดไป"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"ข้าม"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index bde4027..b24e95a 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Isumite"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Tumatakbo ang driving app"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Mag-tap para lumabas sa app sa pagmamaneho."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Pagsasama o aktibong hotspot"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"I-tap upang i-set up."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Naka-disable ang pag-tether"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Bumalik"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Susunod"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Laktawan"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0168ba0..326ace9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Gönder"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sürüş uygulaması çalışıyor"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sürüş uygulamasından çıkmak için dokunun."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering veya hotspot etkin"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Ayarlamak için dokunun."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering devre dışı bırakıldı"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Geri"</string>
     <string name="next_button_label" msgid="6040209156399907780">"İleri"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Atla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 32042ef..8007f9e 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1469,10 +1469,6 @@
     <string name="submit" msgid="862795280643405865">"Надіслати"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Працює додаток для автомобілів"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Торкніться, щоб вийти з додатка для автомобілів."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Прив\'язка чи точка дост. активна"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Торкніться, щоб налаштувати."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Використання телефона в режимі модема вимкнено"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Далі"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Пропустити"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1dd8911..312aed7d 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"جمع کرائیں"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ڈرائیونگ ایپ چل رہی ہے"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ڈرائیونگ ایپ سے باہر نکلنے کے لئے تھپتھپائيں۔"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"ٹیدرنگ غیر فعال ہے"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
     <string name="back_button_label" msgid="4078224038025043387">"واپس جائیں"</string>
     <string name="next_button_label" msgid="6040209156399907780">"اگلا"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"نظر انداز کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4b7847d..c7987d1 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Yuborish"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Avtomobil ilovasi ishlayapti"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Avtomobil ilovasidan chiqish uchun bosing"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Modem rejimi yoniq"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Sozlash uchun bosing."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Modem rejimi faolsizlantirildi"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Orqaga"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Keyingisi"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Tashlab o‘tish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 971cf34..13c75f6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Gửi"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Ứng dụng lái xe đang chạy"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Nhấn để thoát khỏi ứng dụng lái xe."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Nhấn để thiết lập."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Đã tắt tính năng chia sẻ kết nối"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Quay lại"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Tiếp theo"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Bỏ qua"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d7dd8b1..1df2a40 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"提交"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"驾驶应用正在运行"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"点按即可退出驾驶应用。"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"网络共享或热点已启用"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"点按即可进行设置。"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"网络共享已停用"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"请与您的管理员联系以了解详情"</string>
     <string name="back_button_label" msgid="4078224038025043387">"上一步"</string>
     <string name="next_button_label" msgid="6040209156399907780">"下一步"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"跳过"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1a37844..d4d1f7c 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"提交"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"駕駛應用程式執行中"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"輕按即可退出駕駛應用程式。"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"已啟用網絡共享或熱點"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"輕按即可設定。"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"網絡共享已停用"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"請聯絡您的管理員以瞭解詳情"</string>
     <string name="back_button_label" msgid="4078224038025043387">"返回"</string>
     <string name="next_button_label" msgid="6040209156399907780">"繼續"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5e655ac..9997b68 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"提交"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"行車應用程式執行中"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"輕觸即可結束行車應用程式。"</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"網路共用或無線基地台已啟用"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"輕觸即可進行設定。"</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"數據連線已停用"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"詳情請洽你的管理員"</string>
     <string name="back_button_label" msgid="4078224038025043387">"返回"</string>
     <string name="next_button_label" msgid="6040209156399907780">"繼續"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5f8d61e..5838a93 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1429,10 +1429,6 @@
     <string name="submit" msgid="862795280643405865">"Hambisa"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Uhlelo lokusebenza lokushayela luyasebenza"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Thepha ukuze uphume kuhlelo lokusebenza lokushayela."</string>
-    <string name="tethered_notification_title" msgid="2700523927485687353">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
-    <string name="tethered_notification_message" msgid="6228080755828019453">"Thepha ukuze usethe."</string>
-    <string name="disable_tether_notification_title" msgid="7158047514545848391">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
-    <string name="disable_tether_notification_message" msgid="98281313984014775">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
     <string name="back_button_label" msgid="4078224038025043387">"Emuva"</string>
     <string name="next_button_label" msgid="6040209156399907780">"Okulandelayo"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Yeqa"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6741fea..fc9fa85 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -508,7 +508,7 @@
         -->
     <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string>
     <!-- Sent in response to a provisioning check. The caller must hold the
-         permission android.permission.CONNECTIVITY_INTERNAL for Settings to
+         permission android.permission.TETHER_PRIVILEGED for Settings to
          receive this response.
 
          See config_mobile_hotspot_provision_response
@@ -2148,8 +2148,6 @@
     <integer name="config_dreamsBatteryLevelDrainCutoff">5</integer>
     <!-- Limit of how long the device can remain unlocked due to attention checking.  -->
     <integer name="config_attentionMaximumExtension">330000</integer> <!-- 5 minutes and 30 sec.-->
-    <!-- How long we should wait until we give up on receiving an attention API callback.  -->
-    <integer name="config_attentionApiTimeout">2000</integer> <!-- 2 seconds -->
 
     <!-- ComponentName of a dream to show whenever the system would otherwise have
          gone to sleep.  When the PowerManager is asked to go to sleep, it will instead
@@ -2363,9 +2361,11 @@
          1 (0b001) - enforce (only install system packages if they are whitelisted)
          2 (0b010) - log (log when a non-whitelisted package is run)
          4 (0b100) - treat any package not mentioned in the whitelist file as implicitly whitelisted
+         8 (0b1000) - ignore OTAs (don't install system packages during OTAs)
         Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in
         frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java -->
-    <integer name="config_userTypePackageWhitelistMode">5</integer> <!-- 0b101 -->
+    <integer name="config_userTypePackageWhitelistMode">13</integer> <!-- 0b1101 -->
+    <!-- TODO(b/143200798): Change to value 5, i.e. 0b0101, when b/143200798 is resolved. -->
 
     <!-- Whether UI for multi user should be shown -->
     <bool name="config_enableMultiUserUI">false</bool>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index b52f535..ddd9ba4 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -220,4 +220,7 @@
 
   <!-- Accessibility action identifier for {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TAKE_SCREENSHOT}. -->
   <item type="id" name="accessibilitySystemActionTakeScreenshot" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PRESS_AND_HOLD}. -->
+  <item type="id" name="accessibilityActionPressAndHold" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cde95aa..57e0e1b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3013,6 +3013,7 @@
     </public-group>
 
     <public-group type="id" first-id="0x0102004a">
+      <public name="accessibilityActionPressAndHold" />
     </public-group>
 
     <public-group type="string" first-id="0x01040025">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7310daf..dcf7379 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3838,18 +3838,6 @@
     <string name="car_mode_disable_notification_title">Driving app is running</string>
     <string name="car_mode_disable_notification_message">Tap to exit driving app.</string>
 
-    <!-- Strings for tethered notification -->
-    <!-- Shown when the device is tethered -->
-    <string name="tethered_notification_title">Tethering or hotspot active</string>
-    <string name="tethered_notification_message">Tap to set up.</string>
-
-    <!-- Strings for tether disabling notification -->
-    <!-- This notification is shown when tethering has been disabled on a user's device.
-    The device is managed by the user's employer. Tethering can't be turned on unless the
-    IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
-    <string name="disable_tether_notification_title">Tethering is disabled</string>
-    <string name="disable_tether_notification_message">Contact your admin for details</string>
-
     <!--  Strings for possible PreferenceActivity Back/Next buttons -->
     <string name="back_button_label">Back</string>
     <string name="next_button_label">Next</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 130b31f..c6c9094 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1893,9 +1893,6 @@
   <java-symbol type="drawable" name="stat_sys_battery_charge" />
   <java-symbol type="drawable" name="stat_sys_battery_unknown" />
   <java-symbol type="drawable" name="stat_sys_data_usb" />
-  <java-symbol type="drawable" name="stat_sys_tether_bluetooth" />
-  <java-symbol type="drawable" name="stat_sys_tether_general" />
-  <java-symbol type="drawable" name="stat_sys_tether_usb" />
   <java-symbol type="drawable" name="stat_sys_throttled" />
   <java-symbol type="drawable" name="vpn_connected" />
   <java-symbol type="drawable" name="vpn_disconnected" />
@@ -2071,10 +2068,6 @@
   <java-symbol type="string" name="select_keyboard_layout_notification_message" />
   <java-symbol type="string" name="smv_application" />
   <java-symbol type="string" name="smv_process" />
-  <java-symbol type="string" name="tethered_notification_message" />
-  <java-symbol type="string" name="tethered_notification_title" />
-  <java-symbol type="string" name="disable_tether_notification_message" />
-  <java-symbol type="string" name="disable_tether_notification_title" />
   <java-symbol type="string" name="adb_debugging_notification_channel_tv" />
   <java-symbol type="string" name="usb_accessory_notification_title" />
   <java-symbol type="string" name="usb_mtp_notification_title" />
@@ -3652,7 +3645,6 @@
 
   <!-- For Attention Service -->
   <java-symbol type="integer" name="config_attentionMaximumExtension" />
-  <java-symbol type="integer" name="config_attentionApiTimeout" />
 
   <java-symbol type="string" name="config_incidentReportApproverPackage" />
   <java-symbol type="array" name="config_restrictedImagesServices" />
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index a391e1f..796d7e8 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -73,9 +73,9 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
+    <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
+    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
new file mode 100644
index 0000000..02ed0ed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.app.timezonedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+public class ManualTimeZoneSuggestionTest {
+
+    private static final String ARBITRARY_ZONE_ID1 = "Europe/London";
+    private static final String ARBITRARY_ZONE_ID2 = "Europe/Paris";
+
+    @Test
+    public void testEquals() {
+        ManualTimeZoneSuggestion one = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertEquals(one, one);
+
+        ManualTimeZoneSuggestion two = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        ManualTimeZoneSuggestion three = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID2);
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertRoundTripParcelable(suggestion);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        suggestion.addDebugInfo("This is debug info");
+        ManualTimeZoneSuggestion rtSuggestion = roundTripParcelable(suggestion);
+        assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java b/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java
new file mode 100644
index 0000000..0073d86
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.app.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.reflect.Field;
+
+/** Utility methods related to {@link Parcelable} objects used in several tests. */
+public final class ParcelableTestSupport {
+
+    private ParcelableTestSupport() {}
+
+    /** Returns the result of parceling and unparceling the argument. */
+    @SuppressWarnings("unchecked")
+    public static <T extends Parcelable> T roundTripParcelable(T parcelable) {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeTypedObject(parcelable, 0);
+        parcel.setDataPosition(0);
+
+        Parcelable.Creator<T> creator;
+        try {
+            Field creatorField = parcelable.getClass().getField("CREATOR");
+            creator = (Parcelable.Creator<T>) creatorField.get(null);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+        T toReturn = parcel.readTypedObject(creator);
+        parcel.recycle();
+        return toReturn;
+    }
+
+    public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
+        assertEquals(instance, roundTripParcelable(instance));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
index ae91edc..0108a0b 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
@@ -16,13 +16,13 @@
 
 package android.app.timezonedetector;
 
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.os.Parcel;
-import android.os.Parcelable;
-
 import org.junit.Test;
 
 public class PhoneTimeZoneSuggestionTest {
@@ -152,19 +152,4 @@
         assertEquals(suggestion1, suggestion1_2);
         assertTrue(suggestion1_2.getDebugInfo().contains(debugString));
     }
-
-    private static void assertRoundTripParcelable(PhoneTimeZoneSuggestion instance) {
-        assertEquals(instance, roundTripParcelable(instance));
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T extends Parcelable> T roundTripParcelable(T one) {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeTypedObject(one, 0);
-        parcel.setDataPosition(0);
-
-        T toReturn = (T) parcel.readTypedObject(PhoneTimeZoneSuggestion.CREATOR);
-        parcel.recycle();
-        return toReturn;
-    }
 }
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
new file mode 100644
index 0000000..c4080e8
--- /dev/null
+++ b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.app.PropertyInvalidatedCache;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class PropertyInvalidatedCacheTest extends TestCase {
+    private static final String KEY = "sys.testkey";
+    private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+
+    private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+        TestCache() {
+            this(KEY);
+        }
+
+        TestCache(String key) {
+            super(4, key);
+        }
+
+        @Override
+        protected String recompute(Integer qv) {
+            mRecomputeCount += 1;
+            return "foo" + qv.toString();
+        }
+
+        int getRecomputeCount() {
+            return mRecomputeCount;
+        }
+
+        private int mRecomputeCount = 0;
+    }
+
+    @Override
+    protected void setUp() {
+        SystemProperties.set(KEY, "");
+    }
+
+    @SmallTest
+    public void testCacheRecompute() throws Exception {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo6", cache.query(6));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCacheInitialState() throws Exception {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCachePropertyUnset() throws Exception {
+        TestCache cache = new TestCache(UNSET_KEY);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCacheDisableState() throws Exception {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+        cache.disableSystemWide();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(5, cache.getRecomputeCount());
+        cache.invalidateCache();  // Should not reenable
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(7, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testRefreshSameObject() throws Exception {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            protected String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                return oldResult;
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertSame(result1, result2);
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(1, refreshCount[0]);
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, refreshCount[0]);
+    }
+
+    @SmallTest
+    public void testRefreshInvalidateRace() throws Exception {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            protected String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                invalidateCache();
+                return new String(oldResult);
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertEquals(result1, result2);
+        assertNotSame(result1, result2);
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testLocalProcessDisable() throws Exception {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(cache.isDisabledLocal(), false);
+        cache.disableLocal();
+        assertEquals(cache.isDisabledLocal(), true);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+}
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index 4ef4b1f..8e0f1d2 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -30,7 +30,6 @@
 
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index dd4f238c..c49d663 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -73,7 +73,7 @@
 
 prebuilt_etc {
     name: "privapp_whitelist_com.android.emergency",
-    product_specific: true,
+    system_ext_specific: true,
     sub_dir: "permissions",
     src: "com.android.emergency.xml",
     filename_from_src: true,
@@ -96,7 +96,7 @@
 
 prebuilt_etc {
     name: "privapp_whitelist_com.android.provision",
-    product_specific: true,
+    system_ext_specific: true,
     sub_dir: "permissions",
     src: "com.android.provision.xml",
     filename_from_src: true,
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index be10a2c..cffb1ba 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -45,6 +45,10 @@
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.carrierconfig.xml)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.carrierconfig.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.emergency.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.emergency.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.provision.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.provision.xml)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 10fc59d..604b407 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -17,7 +17,7 @@
 <!--
 This XML file declares which system packages should be initially installed for new users based on
 their user type. All system packages on the device should ideally have an entry in an xml file
-(keys by its manifest name).
+(keyed by its manifest name).
 
 Base user-types (every user will be at least one of these types) are:
   SYSTEM    (user 0)
@@ -46,7 +46,7 @@
 1. For a system package to be pre-installed only in user 0:
 
    <install-in-user-type package="com.android.example">
-       <install-in user-type="SYSTEM">
+       <install-in user-type="SYSTEM" />
    </install-in-user-type>
 
 
@@ -54,8 +54,8 @@
 installed on any user of type type FULL or PROFILE (since this covers all human users):
 
    <install-in-user-type package="com.android.example">
-       <install-in user-type="FULL">
-       <install-in user-type="PROFILE">
+       <install-in user-type="FULL" />
+       <install-in user-type="PROFILE" />
    </install-in-user-type>
 
 
@@ -63,22 +63,22 @@
 wallpaper app, since profiles cannot display wallpaper):
 
    <install-in-user-type package="com.android.example">
-       <install-in user-type="FULL">
+       <install-in user-type="FULL" />
    </install-in-user-type>
 
 
 Some system packages truly are required to be on all users, regardless of type, in which case use:
    <install-in-user-type package="com.android.example">
        <install-in user-type="SYSTEM">
-       <install-in user-type="FULL">
-       <install-in user-type="PROFILE">
+       <install-in user-type="FULL" />
+       <install-in user-type="PROFILE" />
    </install-in-user-type>
 
 More fine-grained options are also available by specifying individual user types.
 E.g.
    <install-in-user-type package="com.android.example">
-       <install-in user-type="android.os.usertype.profile.MANAGED">
-       <install-in user-type="android.os.usertype.full.GUEST">
+       <install-in user-type="android.os.usertype.profile.MANAGED" />
+       <install-in user-type="android.os.usertype.full.GUEST" />
        <install-in user-type="SYSTEM">
    </install-in-user-type>
 which installs this package on any user whose user type is a managed profile or a guest, or is of
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
new file mode 100644
index 0000000..613ce90
--- /dev/null
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 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.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Shader that calculates pixel output with a program (fragment shader) running on a GPU.
+ * @hide
+ */
+public class RuntimeShader extends Shader {
+
+    private static class NoImagePreloadHolder {
+        public static final NativeAllocationRegistry sRegistry =
+                NativeAllocationRegistry.createMalloced(
+                RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
+    }
+
+    private byte[] mUniforms;
+
+    /**
+     * Current native shader factory instance.
+     */
+    private long mNativeInstanceRuntimeShaderFactory;
+
+    /**
+     * Creates a new RuntimeShader.
+     *
+     * @param sksl The text of SKSL program to run on the GPU.
+     * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+     *                 on number of uniforms declared by sksl.
+     * @param isOpaque True if all pixels have alpha 1.0f.
+     */
+    public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) {
+        this(sksl, uniforms, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque,
+            ColorSpace colorSpace) {
+        super(colorSpace);
+        mUniforms = uniforms;
+        mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl, isOpaque);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
+                mNativeInstanceRuntimeShaderFactory);
+    }
+
+    /**
+     * Sets new value for shader parameters.
+     *
+     * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+     *                 on number of uniforms declared by mSksl.
+     */
+    public void updateUniforms(@Nullable byte[] uniforms) {
+        mUniforms = uniforms;
+        discardNativeInstance();
+    }
+
+    @Override
+    long createNativeInstance(long nativeMatrix) {
+        return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
+                colorSpace().getNativeInstance());
+    }
+
+    private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
+            long colorSpaceHandle);
+
+    private static native long nativeCreateShaderFactory(String sksl, boolean isOpaque);
+
+    private static native long nativeGetFinalizer();
+}
+
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f25910b..572fa8c 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -68,6 +68,9 @@
     /** Key prefix for WIFI. */
     public static final String WIFI = "WIFI_";
 
+    /** Key prefix for App Source certificates. */
+    public static final String APP_SOURCE_CERTIFICATE = "FSV_";
+
     /** Key containing suffix of lockdown VPN profile. */
     public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN";
 
@@ -80,6 +83,9 @@
     /** Name of WIFI certificate usage. */
     public static final String CERTIFICATE_USAGE_WIFI = "wifi";
 
+    /** Name of App Source certificate usage. */
+    public static final String CERTIFICATE_USAGE_APP_SOURCE = "appsrc";
+
     /** Data type for public keys. */
     public static final String EXTRA_PUBLIC_KEY = "KEY";
 
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 4f52a88..8765719 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -67,7 +67,6 @@
                 "BackupData.cpp",
                 "BackupHelpers.cpp",
                 "CursorWindow.cpp",
-                "DisplayEventDispatcher.cpp",
             ],
             shared_libs: [
                 "libziparchive",
@@ -75,7 +74,6 @@
                 "libbinder",
                 "liblog",
                 "libcutils",
-                "libgui",
                 "libutils",
                 "libz",
             ],
diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp
deleted file mode 100644
index d8a3f426..0000000
--- a/libs/androidfw/DisplayEventDispatcher.cpp
+++ /dev/null
@@ -1,153 +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.
- */
-
-#define LOG_TAG "DisplayEventDispatcher"
-
-#include <cinttypes>
-#include <cstdint>
-
-#include <androidfw/DisplayEventDispatcher.h>
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-
-#include <utils/Timers.h>
-
-namespace android {
-
-// Number of events to read at a time from the DisplayEventDispatcher pipe.
-// The value should be large enough that we can quickly drain the pipe
-// using just a few large reads.
-static const size_t EVENT_BUFFER_SIZE = 100;
-
-DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
-        ISurfaceComposer::VsyncSource vsyncSource,
-        ISurfaceComposer::ConfigChanged configChanged) :
-        mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
-    ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
-}
-
-status_t DisplayEventDispatcher::initialize() {
-    status_t result = mReceiver.initCheck();
-    if (result) {
-        ALOGW("Failed to initialize display event receiver, status=%d", result);
-        return result;
-    }
-
-    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
-            this, NULL);
-    if (rc < 0) {
-        return UNKNOWN_ERROR;
-    }
-    return OK;
-}
-
-void DisplayEventDispatcher::dispose() {
-    ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
-
-    if (!mReceiver.initCheck()) {
-        mLooper->removeFd(mReceiver.getFd());
-    }
-}
-
-status_t DisplayEventDispatcher::scheduleVsync() {
-    if (!mWaitingForVsync) {
-        ALOGV("dispatcher %p ~ Scheduling vsync.", this);
-
-        // Drain all pending events.
-        nsecs_t vsyncTimestamp;
-        PhysicalDisplayId vsyncDisplayId;
-        uint32_t vsyncCount;
-        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
-            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
-                    this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
-        }
-
-        status_t status = mReceiver.requestNextVsync();
-        if (status) {
-            ALOGW("Failed to request next vsync, status=%d", status);
-            return status;
-        }
-
-        mWaitingForVsync = true;
-    }
-    return OK;
-}
-
-int DisplayEventDispatcher::handleEvent(int, int events, void*) {
-    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
-        ALOGE("Display event receiver pipe was closed or an error occurred.  "
-                "events=0x%x", events);
-        return 0; // remove the callback
-    }
-
-    if (!(events & Looper::EVENT_INPUT)) {
-        ALOGW("Received spurious callback for unhandled poll event.  "
-                "events=0x%x", events);
-        return 1; // keep the callback
-    }
-
-    // Drain all pending events, keep the last vsync.
-    nsecs_t vsyncTimestamp;
-    PhysicalDisplayId vsyncDisplayId;
-    uint32_t vsyncCount;
-    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
-        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
-                ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
-                this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
-        mWaitingForVsync = false;
-        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
-    }
-
-    return 1; // keep the callback
-}
-
-bool DisplayEventDispatcher::processPendingEvents(
-        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
-    bool gotVsync = false;
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    ssize_t n;
-    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
-        for (ssize_t i = 0; i < n; i++) {
-            const DisplayEventReceiver::Event& ev = buf[i];
-            switch (ev.header.type) {
-            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                // Later vsync events will just overwrite the info from earlier
-                // ones. That's fine, we only care about the most recent.
-                gotVsync = true;
-                *outTimestamp = ev.header.timestamp;
-                *outDisplayId = ev.header.displayId;
-                *outCount = ev.vsync.count;
-                break;
-            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
-                dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
-                break;
-            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-                dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId);
-                break;
-            default:
-                ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
-                break;
-            }
-        }
-    }
-    if (n < 0) {
-        ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
-    }
-    return gotVsync;
-}
-}
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
deleted file mode 100644
index 8bc2520..0000000
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ /dev/null
@@ -1,51 +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.
- */
-
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-
-namespace android {
-
-class DisplayEventDispatcher : public LooperCallback {
-public:
-    explicit DisplayEventDispatcher(const sp<Looper>& looper,
-            ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
-            ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress);
-
-    status_t initialize();
-    void dispose();
-    status_t scheduleVsync();
-
-protected:
-    virtual ~DisplayEventDispatcher() = default;
-
-private:
-    sp<Looper> mLooper;
-    DisplayEventReceiver mReceiver;
-    bool mWaitingForVsync;
-
-    virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
-    virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                 bool connected) = 0;
-    virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                       int32_t configId) = 0;
-
-    virtual int handleEvent(int receiveFd, int events, void* data);
-    bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
-                              uint32_t* outCount);
-};
-}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 217b0c4..cd908354 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -135,8 +135,7 @@
     bool setFillPath = properties.getFillGradient() != nullptr ||
                        properties.getFillColor() != SK_ColorTRANSPARENT;
     if (setFillPath) {
-        SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType());
-        outPath->setFillType(ft);
+        outPath->setFillType(static_cast<SkPathFillType>(properties.getFillType()));
     }
     return *outPath;
 }
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 9013a96..70abbb3 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -16,9 +16,11 @@
 
 package android.location;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
+import android.hardware.gnss.V1_0.IGnssMeasurementCallback.GnssMeasurementFlags;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -37,6 +39,7 @@
     private long mReceivedSvTimeNanos;
     private long mReceivedSvTimeUncertaintyNanos;
     private double mCn0DbHz;
+    private double mBasebandCn0DbHz;
     private double mPseudorangeRateMetersPerSecond;
     private double mPseudorangeRateUncertaintyMetersPerSecond;
     private int mAccumulatedDeltaRangeState;
@@ -51,16 +54,20 @@
     private double mAutomaticGainControlLevelInDb;
     @NonNull private String mCodeType;
 
-    // The following enumerations must be in sync with the values declared in gps.h
+    // The following enumerations must be in sync with the values declared in GNSS HAL.
 
     private static final int HAS_NO_FLAGS = 0;
-    private static final int HAS_SNR = (1<<0);
-    private static final int HAS_CARRIER_FREQUENCY = (1<<9);
-    private static final int HAS_CARRIER_CYCLES = (1<<10);
-    private static final int HAS_CARRIER_PHASE = (1<<11);
-    private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
-    private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
+    private static final int HAS_SNR = GnssMeasurementFlags.HAS_SNR;
+    private static final int HAS_CARRIER_FREQUENCY = GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
+    private static final int HAS_CARRIER_CYCLES = GnssMeasurementFlags.HAS_CARRIER_CYCLES;
+    private static final int HAS_CARRIER_PHASE = GnssMeasurementFlags.HAS_CARRIER_PHASE;
+    private static final int HAS_CARRIER_PHASE_UNCERTAINTY =
+            GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
+    private static final int HAS_AUTOMATIC_GAIN_CONTROL =
+            GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
+
     private static final int HAS_CODE_TYPE = (1 << 14);
+    private static final int HAS_BASEBAND_CN0 = (1 << 15);
 
     /**
      * The status of the multipath indicator.
@@ -240,6 +247,7 @@
         mReceivedSvTimeNanos = measurement.mReceivedSvTimeNanos;
         mReceivedSvTimeUncertaintyNanos = measurement.mReceivedSvTimeUncertaintyNanos;
         mCn0DbHz = measurement.mCn0DbHz;
+        mBasebandCn0DbHz = measurement.mBasebandCn0DbHz;
         mPseudorangeRateMetersPerSecond = measurement.mPseudorangeRateMetersPerSecond;
         mPseudorangeRateUncertaintyMetersPerSecond =
                 measurement.mPseudorangeRateUncertaintyMetersPerSecond;
@@ -788,6 +796,49 @@
     }
 
     /**
+     * Returns {@code true} if {@link #getBasebandCn0DbHz()} is available, {@code false} otherwise.
+     */
+    public boolean hasBasebandCn0DbHz() {
+        return isFlagSet(HAS_BASEBAND_CN0);
+    }
+
+    /**
+     * Gets the baseband carrier-to-noise density in dB-Hz.
+     *
+     * <p>Typical range: 0-50 dB-Hz.
+     *
+     * <p>The value contains the measured C/N0 for the signal at the baseband. This is typically
+     * a few dB weaker than the value estimated for C/N0 at the antenna port, which is reported
+     * in {@link #getCn0DbHz()}.
+     */
+    @FloatRange(from = 0, to = 50)
+    public double getBasebandCn0DbHz() {
+        return mBasebandCn0DbHz;
+    }
+
+    /**
+     * Sets the baseband carrier-to-noise density in dB-Hz.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setBasebandCn0DbHz(double value) {
+        setFlag(HAS_BASEBAND_CN0);
+        mBasebandCn0DbHz = value;
+    }
+
+    /**
+     * Resets the baseband carrier-to-noise density in dB-Hz.
+     *
+     * @hide
+     */
+    @TestApi
+    public void resetBasebandCn0DbHz() {
+        resetFlag(HAS_BASEBAND_CN0);
+        mBasebandCn0DbHz = Double.NaN;
+    }
+
+    /**
      * Gets the Pseudorange rate at the timestamp in m/s.
      *
      * <p>The error estimate for this value is
@@ -1400,6 +1451,7 @@
             gnssMeasurement.mSnrInDb = parcel.readDouble();
             gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
             gnssMeasurement.mCodeType = parcel.readString();
+            gnssMeasurement.mBasebandCn0DbHz = parcel.readDouble();
 
             return gnssMeasurement;
         }
@@ -1433,6 +1485,7 @@
         parcel.writeDouble(mSnrInDb);
         parcel.writeDouble(mAutomaticGainControlLevelInDb);
         parcel.writeString(mCodeType);
+        parcel.writeDouble(mBasebandCn0DbHz);
     }
 
     @Override
@@ -1461,6 +1514,9 @@
 
         builder.append(String.format(format, "Cn0DbHz", mCn0DbHz));
 
+        builder.append(String.format(format, "BasebandCn0DbHz",
+                hasBasebandCn0DbHz() ? mBasebandCn0DbHz : null));
+
         builder.append(String.format(
                 formatWithUncertainty,
                 "PseudorangeRateMetersPerSecond",
@@ -1536,6 +1592,7 @@
         resetSnrInDb();
         resetAutomaticGainControlLevel();
         resetCodeType();
+        resetBasebandCn0DbHz();
     }
 
     private void setFlag(int flag) {
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 81a7ee2..d803f04 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -40,7 +40,6 @@
     void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
     void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
     void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
-    void setControlCategories(IMediaRouterClient client, in List<String> categories);
 
     // Methods for media router 2
     List<MediaRoute2Info> getSystemRoutes();
@@ -56,7 +55,7 @@
      * @param route the route to be selected
      */
     void requestSelectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
-    void setControlCategories2(IMediaRouter2Client client, in List<String> categories);
+    void setControlCategories(IMediaRouter2Client client, in List<String> categories);
 
     void registerManager(IMediaRouter2Manager manager, String packageName);
     void unregisterManager(IMediaRouter2Manager manager);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 7b15d95..d72231f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -49,8 +49,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
@@ -84,7 +82,6 @@
 
         final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
         final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
-        List<String> mControlCategories = Collections.emptyList();
 
         final RouteCategory mSystemCategory;
 
@@ -361,18 +358,6 @@
             return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
         }
 
-        public void setControlCategories(Collection<String> controlCategories) {
-            List<String> newControlCategories = new ArrayList<>(controlCategories);
-            mControlCategories = newControlCategories;
-            if (mClient != null) {
-                try {
-                    mMediaRouterService.setControlCategories(mClient, newControlCategories);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Unable to set control categories.", ex);
-                }
-            }
-        }
-
         private void updatePresentationDisplays(int changedDisplayId) {
             final int count = mRoutes.size();
             for (int i = 0; i < count; i++) {
@@ -421,7 +406,6 @@
                 try {
                     Client client = new Client();
                     mMediaRouterService.registerClientAsUser(client, mPackageName, userId);
-                    mMediaRouterService.setControlCategories(client, mControlCategories);
                     mClient = client;
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to register media router client.", ex);
@@ -1318,20 +1302,6 @@
         sStatic.rebindAsUser(userId);
     }
 
-    //TODO: remove this and Client1Record in MediaRouter2ServiceImpl.
-    /**
-     * Sets the control categories of the application.
-     * Routes that support at least one of the given control categories only exists and are handled
-     * by the media router.
-     *
-     * @hide
-     */
-    public void setControlCategories(@NonNull Collection<String> controlCategories) {
-        Objects.requireNonNull(controlCategories, "control categories must not be null");
-
-        sStatic.setControlCategories(controlCategories);
-    }
-
     static void updateRoute(final RouteInfo info) {
         dispatchRouteChanged(info);
     }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 35cb066..3e6f4c0 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -114,7 +114,7 @@
     @GuardedBy("sLock")
     private MediaRoute2Info mSelectingRoute;
     @GuardedBy("sLock")
-    private Client mClient;
+    private Client2 mClient;
 
     final Handler mHandler;
     volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
@@ -196,10 +196,10 @@
 
         synchronized (sLock) {
             if (mClient == null) {
-                Client client = new Client();
+                Client2 client = new Client2();
                 try {
                     mMediaRouterService.registerClient2(client, mPackageName);
-                    mMediaRouterService.setControlCategories2(client, mControlCategories);
+                    mMediaRouterService.setControlCategories(client, mControlCategories);
                     mClient = client;
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to register media router.", ex);
@@ -288,7 +288,7 @@
     public void requestSelectRoute(@NonNull MediaRoute2Info route) {
         Objects.requireNonNull(route, "route must not be null");
 
-        Client client;
+        Client2 client;
         synchronized (sLock) {
             if (mSelectingRoute == route) {
                 Log.w(TAG, "The route selection request is already sent.");
@@ -318,7 +318,7 @@
         Objects.requireNonNull(route, "route must not be null");
         Objects.requireNonNull(request, "request must not be null");
 
-        Client client;
+        Client2 client;
         synchronized (sLock) {
             client = mClient;
         }
@@ -342,7 +342,7 @@
     public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
-        Client client;
+        Client2 client;
         synchronized (sLock) {
             client = mClient;
         }
@@ -366,7 +366,7 @@
     public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
         Objects.requireNonNull(route, "route must not be null");
 
-        Client client;
+        Client2 client;
         synchronized (sLock) {
             client = mClient;
         }
@@ -398,13 +398,13 @@
         List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
 
         mControlCategories = newControlCategories;
-        Client client;
+        Client2 client;
         synchronized (sLock) {
             client = mClient;
         }
         if (client != null) {
             try {
-                mMediaRouterService.setControlCategories2(client, mControlCategories);
+                mMediaRouterService.setControlCategories(client, mControlCategories);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to set control categories.", ex);
             }
@@ -600,7 +600,7 @@
         }
     }
 
-    class Client extends IMediaRouter2Client.Stub {
+    class Client2 extends IMediaRouter2Client.Stub {
         @Override
         public void notifyRestoreRoute() throws RemoteException {}
 
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
index 378baf4..b65039d3 100644
--- a/media/jni/android_media_MediaDataSource.h
+++ b/media/jni/android_media_MediaDataSource.h
@@ -19,7 +19,7 @@
 
 #include "jni.h"
 
-#include <media/IDataSource.h>
+#include <android/IDataSource.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <utils/Errors.h>
 #include <utils/Mutex.h>
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
index b7cbd97..4fd5153 100644
--- a/media/jni/android_media_Streams.cpp
+++ b/media/jni/android_media_Streams.cpp
@@ -28,67 +28,6 @@
 
 namespace android {
 
-AssetStream::AssetStream(SkStream* stream)
-    : mStream(stream), mPosition(0) {
-}
-
-AssetStream::~AssetStream() {
-}
-
-piex::Error AssetStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (mPosition != offset) {
-        if (!mStream->seek(offset)) {
-            return piex::Error::kFail;
-        }
-    }
-
-    // Read bytes.
-    size_t size = mStream->read((void*)data, length);
-    mPosition = offset + size;
-
-    return size == length ? piex::Error::kOk : piex::Error::kFail;
-}
-
-BufferedStream::BufferedStream(SkStream* stream)
-    : mStream(stream) {
-}
-
-BufferedStream::~BufferedStream() {
-}
-
-piex::Error BufferedStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (offset + length > mStreamBuffer.bytesWritten()) {
-        size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
-        if (sizeToRead <= kMinSizeToRead) {
-            sizeToRead = kMinSizeToRead;
-        }
-
-        void* tempBuffer = malloc(sizeToRead);
-        if (tempBuffer == NULL) {
-          return piex::Error::kFail;
-        }
-
-        size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
-        if (bytesRead != sizeToRead) {
-            free(tempBuffer);
-            return piex::Error::kFail;
-        }
-        mStreamBuffer.write(tempBuffer, bytesRead);
-        free(tempBuffer);
-    }
-
-    // Read bytes.
-    if (mStreamBuffer.read((void*)data, offset, length)) {
-        return piex::Error::kOk;
-    } else {
-        return piex::Error::kFail;
-    }
-}
-
 FileStream::FileStream(const int fd)
     : mPosition(0) {
     mFile = fdopen(fd, "r");
diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h
index d174f9a..800591c 100644
--- a/media/jni/android_media_Streams.h
+++ b/media/jni/android_media_Streams.h
@@ -25,53 +25,9 @@
 #include <utils/KeyedVector.h>
 #include <utils/String8.h>
 #include <utils/StrongPointer.h>
-#include <SkStream.h>
-
 
 namespace android {
 
-class AssetStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    size_t mPosition;
-
-public:
-    explicit AssetStream(SkStream* stream);
-    ~AssetStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class BufferedStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    // Growable memory stream
-    SkDynamicMemoryWStream mStreamBuffer;
-
-    // Minimum size to read on filling the buffer.
-    const size_t kMinSizeToRead = 8192;
-
-public:
-    explicit BufferedStream(SkStream* stream);
-    ~BufferedStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
 class FileStream : public piex::StreamInterface {
 private:
     FILE *mFile;
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index e7d4d90..809e81b 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -179,6 +179,7 @@
 {
     std::lock_guard lock(mLock);
     if (streamID == mStreamID) {
+        ALOGV("%s: track streamID: %d", __func__, streamID);
         if (mAudioTrack != nullptr) {
             if (mState == PLAYING && !mMuted && (mLeftVolume != 0.f || mRightVolume != 0.f)) {
                 setVolume_l(0.f, 0.f);
@@ -202,6 +203,7 @@
 void Stream::stop_l()
 {
     if (mState != IDLE) {
+        ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
         if (mAudioTrack != nullptr) {
             mAudioTrack->stop();
         }
@@ -227,6 +229,7 @@
     LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
     sp<AudioTrack> releaseTracks[2];
     {
+        ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
         // TODO: Do we really want to force a simultaneous synchronization between
         // the stream and its pair?
 
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 64f81d5..79e4d8a 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -148,16 +148,18 @@
         sanityCheckQueue_l();
         // find an available stream, prefer one that has matching sound id.
         if (mAvailableStreams.size() > 0) {
-            newStream = *mAvailableStreams.begin();
             for (auto stream : mAvailableStreams) {
                 if (stream->getSoundID() == soundID) {
                     newStream = stream;
+                    ALOGV("%s: found soundID %d in available queue", __func__, soundID);
                     break;
                 }
             }
-            if (newStream != nullptr) {
-                newStream->setStopTimeNs(systemTime());
+            if (newStream == nullptr) {
+                ALOGV("%s: found stream in available queue", __func__);
+                newStream = *mAvailableStreams.begin();
             }
+            newStream->setStopTimeNs(systemTime());
             fromAvailableQueue = true;
         }
 
@@ -166,10 +168,12 @@
             for (auto [unused , stream] : mRestartStreams) {
                 if (!stream->getPairStream()->hasSound()) {
                     if (stream->getSoundID() == soundID) {
+                        ALOGV("%s: found soundID %d in restart queue", __func__, soundID);
                         newStream = stream;
                         fromAvailableQueue = false;
                         break;
                     } else if (newStream == nullptr) {
+                        ALOGV("%s: found stream in restart queue", __func__);
                         newStream = stream;
                     }
                 }
@@ -183,6 +187,7 @@
                     if (newStream == nullptr
                             || newStream->getPriority() > stream->getPriority()) {
                         newStream = stream;
+                        ALOGV("%s: found stream in active queue", __func__);
                     }
                 }
             }
@@ -195,6 +200,7 @@
         if (newStream == nullptr) {
             for (auto [unused, stream] : mRestartStreams) {
                 if (stream->getPairPriority() <= priority) {
+                    ALOGV("%s: evict stream from restart queue", __func__);
                     newStream = stream;
                     break;
                 }
@@ -210,6 +216,8 @@
 
         Stream *pairStream = newStream->getPairStream();
         streamID = getNextIdForStream(pairStream);
+        ALOGV("%s: newStream:%p  pairStream:%p, streamID:%d",
+                __func__, newStream, pairStream, streamID);
         pairStream->setPlay(
                 streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
         if (fromAvailableQueue && kPlayOnCallingThread) {
diff --git a/media/jni/soundpool/tests/build_and_run.sh b/media/jni/soundpool/tests/build_and_run.sh
index 741f2ef..72fd528 100755
--- a/media/jni/soundpool/tests/build_and_run.sh
+++ b/media/jni/soundpool/tests/build_and_run.sh
@@ -23,7 +23,10 @@
 adb push $OUT/system/bin/soundpool_stress /system/bin
 
 # test SoundPool playback of all the UI sound samples (loaded twice) looping 10s 1 thread.
-#adb shell /system/bin/soundpool_stress -l -1 $uidir/*.ogg $uidir/*.ogg
+adb shell /system/bin/soundpool_stress -l -1 $uidir/*.ogg $uidir/*.ogg
+
+# test SoundPool playback of all the UI sound samples (repeating 3 times) looping 10s 1 thread.
+adb shell /system/bin/soundpool_stress -l 1 -r 3 $uidir/*.ogg
 
 # performance test SoundPool playback of all the UI sound samples (x2)
 # 1 iterations, looping, 1 second playback, 4 threads.
diff --git a/media/jni/soundpool/tests/soundpool_stress.cpp b/media/jni/soundpool/tests/soundpool_stress.cpp
index 212662f..7d9b6a2 100644
--- a/media/jni/soundpool/tests/soundpool_stress.cpp
+++ b/media/jni/soundpool/tests/soundpool_stress.cpp
@@ -49,6 +49,7 @@
     printf("    -i #iterations, default 1\n");
     printf("    -l #loop looping mode, -1 forever\n");
     printf("    -p #playback_seconds, default 10\n");
+    printf("    -r #repeat soundIDs (0 or more times), default 0\n");
     printf("    -s #streams for concurrent sound playback, default 20\n");
     printf("    -t #threads, default 1\n");
     printf("    -z #snoozeSec after stopping, -1 forever, default 0\n");
@@ -112,7 +113,7 @@
 }
 
 void testStreams(SoundPool *soundPool, const std::vector<const char *> &filenames,
-        int loop, int playSec)
+                 int loop, int repeat, int playSec)
 {
     const int64_t startTimeNs = systemTime();
     std::vector<int32_t> soundIDs;
@@ -153,23 +154,25 @@
     // TODO: Use SoundPool::setCallback() for wait
 
     for (int32_t soundID : soundIDs) {
-        while (true) {
-            const int32_t streamID =
+        for (int i = 0; i <= repeat; ++i) {
+            while (true) {
+                const int32_t streamID =
                     soundPool->play(soundID, silentVol, silentVol, priority, 0 /*loop*/, rate);
-            if (streamID != 0) {
-                const int32_t events = gCallbackManager.getNumberEvents(soundID);
-                if (events != 1) {
-                   printf("WARNING: successful play for streamID:%d soundID:%d"
-                          " but callback events(%d) != 1\n", streamID, soundID, events);
-                   ++gWarnings;
+                if (streamID != 0) {
+                    const int32_t events = gCallbackManager.getNumberEvents(soundID);
+                    if (events != 1) {
+                       printf("WARNING: successful play for streamID:%d soundID:%d"
+                              " but callback events(%d) != 1\n", streamID, soundID, events);
+                       ++gWarnings;
+                    }
+                    soundPool->stop(streamID);
+                    break;
                 }
-                soundPool->stop(streamID);
-                break;
+                usleep(1000);
             }
-            usleep(1000);
+            printf("[%d]", soundID);
+            fflush(stdout);
         }
-        printf("[%d]", soundID);
-        fflush(stdout);
     }
 
     const int64_t loadTimeNs = systemTime();
@@ -178,14 +181,17 @@
     // check and play (overlap with above).
     std::vector<int32_t> streamIDs;
     for (int32_t soundID : soundIDs) {
-        printf("\nplaying soundID=%d", soundID);
-        const int32_t streamID = soundPool->play(soundID, maxVol, maxVol, priority, loop, rate);
-        if (streamID == 0) {
-            printf(" failed!  ERROR");
-            ++gErrors;
-        } else {
-            printf(" streamID=%d", streamID);
-            streamIDs.emplace_back(streamID);
+        for (int i = 0; i <= repeat; ++i) {
+            printf("\nplaying soundID=%d", soundID);
+            const int32_t streamID =
+                    soundPool->play(soundID, maxVol, maxVol, priority, loop, rate);
+            if (streamID == 0) {
+                printf(" failed!  ERROR");
+                ++gErrors;
+            } else {
+                printf(" streamID=%d", streamID);
+                streamIDs.emplace_back(streamID);
+            }
         }
     }
     const int64_t playTimeNs = systemTime();
@@ -217,9 +223,10 @@
     int loop = 0;        // disable looping
     int maxStreams = 40; // change to have more concurrent playback streams
     int playSec = 10;
+    int repeat = 0;
     int snoozeSec = 0;
     int threadCount = 1;
-    for (int ch; (ch = getopt(argc, argv, "i:l:p:s:t:z:")) != -1; ) {
+    for (int ch; (ch = getopt(argc, argv, "i:l:p:r:s:t:z:")) != -1; ) {
         switch (ch) {
         case 'i':
             iterations = atoi(optarg);
@@ -230,6 +237,9 @@
         case 'p':
             playSec = atoi(optarg);
             break;
+        case 'r':
+            repeat = atoi(optarg);
+            break;
         case 's':
             maxStreams = atoi(optarg);
             break;
@@ -280,7 +290,7 @@
         printf("testing %zu threads\n", threads.size());
         for (auto &thread : threads) {
             thread = std::async(std::launch::async,
-                    [&]{ testStreams(soundPool.get(), filenames, loop, playSec);});
+                    [&]{ testStreams(soundPool.get(), filenames, loop, repeat, playSec);});
         }
         // automatically joins.
     }
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index e53446e..d245f67 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -32,4 +32,6 @@
     <!-- Timeout values in milliseconds for displaying volume dialog-->
     <integer name="car_volume_dialog_display_normal_timeout">3000</integer>
     <integer name="car_volume_dialog_display_hovering_timeout">16000</integer>
+    <integer name="car_volume_dialog_display_expanded_normal_timeout">6000</integer>
+    <integer name="car_volume_dialog_display_expanded_hovering_timeout">32000</integer>
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
index f2bf811..6056ddf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
+++ b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "auto-end-to-end-postsubmit": [
+  "auto-end-to-end-presubmit": [
     {
       "name": "AndroidAutoUiTests",
       "options" : [
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 367959e..c9ac765 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -90,6 +90,8 @@
     private final KeyguardManager mKeyguard;
     private final int mNormalTimeout;
     private final int mHoveringTimeout;
+    private final int mExpNormalTimeout;
+    private final int mExpHoveringTimeout;
 
     private Window mWindow;
     private CustomDialog mDialog;
@@ -176,6 +178,10 @@
                 R.integer.car_volume_dialog_display_normal_timeout);
         mHoveringTimeout = mContext.getResources().getInteger(
                 R.integer.car_volume_dialog_display_hovering_timeout);
+        mExpNormalTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_expanded_normal_timeout);
+        mExpHoveringTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_expanded_hovering_timeout);
     }
 
     /** Sets a {@link CarServiceProvider} which connects to the audio service. */
@@ -290,7 +296,7 @@
         mShowing = true;
         clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
         mDialog.show();
-        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
     }
 
     private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) {
@@ -313,7 +319,11 @@
     }
 
     private int computeTimeoutH() {
-        return mHovering ? mHoveringTimeout : mNormalTimeout;
+        if (mExpanded) {
+            return mHovering ? mExpHoveringTimeout : mExpNormalTimeout;
+        } else {
+            return mHovering ? mHoveringTimeout : mNormalTimeout;
+        }
     }
 
     private void dismissH(int reason) {
@@ -349,7 +359,7 @@
                 }, DISMISS_DELAY_IN_MILLIS))
                 .start();
 
-        Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+        Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
     }
 
     private void loadAudioUsageItems() {
@@ -532,6 +542,7 @@
         public void onClick(final View v) {
             mExpandIcon = v;
             toggleDialogExpansion(true);
+            rescheduleTimeoutH();
         }
     }
 
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 142078e..9e49826 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -32,9 +32,11 @@
 import static android.os.image.DynamicSystemClient.STATUS_NOT_STARTED;
 import static android.os.image.DynamicSystemClient.STATUS_READY;
 
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_CANCELLED;
 import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
-import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_INVALID_URL;
 import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_IO;
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_FORMAT;
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_URL;
 import static com.android.dynsystem.InstallationAsyncTask.RESULT_OK;
 
 import android.app.Notification;
@@ -66,11 +68,10 @@
  * cancel and confirm commnands.
  */
 public class DynamicSystemInstallationService extends Service
-        implements InstallationAsyncTask.InstallStatusListener {
+        implements InstallationAsyncTask.ProgressListener {
 
     private static final String TAG = "DynSystemInstallationService";
 
-
     // TODO (b/131866826): This is currently for test only. Will move this to System API.
     static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
 
@@ -121,9 +122,12 @@
     private DynamicSystemManager mDynSystem;
     private NotificationManager mNM;
 
-    private long mSystemSize;
-    private long mUserdataSize;
-    private long mInstalledSize;
+    private int mNumInstalledPartitions;
+
+    private String mCurrentPartitionName;
+    private long mCurrentPartitionSize;
+    private long mCurrentPartitionInstalledSize;
+
     private boolean mJustCancelledByUser;
 
     // This is for testing only now
@@ -176,8 +180,12 @@
     }
 
     @Override
-    public void onProgressUpdate(long installedSize) {
-        mInstalledSize = installedSize;
+    public void onProgressUpdate(InstallationAsyncTask.Progress progress) {
+        mCurrentPartitionName = progress.mPartitionName;
+        mCurrentPartitionSize = progress.mPartitionSize;
+        mCurrentPartitionInstalledSize = progress.mInstalledSize;
+        mNumInstalledPartitions = progress.mNumInstalledPartitions;
+
         postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
     }
 
@@ -197,11 +205,16 @@
         resetTaskAndStop();
 
         switch (result) {
+            case RESULT_CANCELLED:
+                postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+                break;
+
             case RESULT_ERROR_IO:
                 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
                 break;
 
-            case RESULT_ERROR_INVALID_URL:
+            case RESULT_ERROR_UNSUPPORTED_URL:
+            case RESULT_ERROR_UNSUPPORTED_FORMAT:
                 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
                 break;
 
@@ -211,12 +224,6 @@
         }
     }
 
-    @Override
-    public void onCancelled() {
-        resetTaskAndStop();
-        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
-    }
-
     private void executeInstallCommand(Intent intent) {
         if (!verifyRequest(intent)) {
             Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
@@ -234,12 +241,13 @@
         }
 
         String url = intent.getDataString();
-        mSystemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
-        mUserdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
+        long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
+        long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
         mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
 
+        // TODO: better constructor or builder
         mInstallTask = new InstallationAsyncTask(
-                url, mSystemSize, mUserdataSize, this, mDynSystem, this);
+                url, systemSize, userdataSize, this, mDynSystem, this);
 
         mInstallTask.execute();
 
@@ -257,7 +265,7 @@
         mJustCancelledByUser = true;
 
         if (mInstallTask.cancel(false)) {
-            // Will cleanup and post status in onCancelled()
+            // Will cleanup and post status in onResult()
             Log.d(TAG, "Cancel request filed successfully");
         } else {
             Log.e(TAG, "Trying to cancel installation while it's already completed.");
@@ -288,7 +296,7 @@
     private void executeRebootToDynSystemCommand() {
         boolean enabled = false;
 
-        if (mInstallTask != null && mInstallTask.getResult() == RESULT_OK) {
+        if (mInstallTask != null && mInstallTask.isCompleted()) {
             enabled = mInstallTask.commit();
         } else if (isDynamicSystemInstalled()) {
             enabled = mDynSystem.setEnable(true, true);
@@ -380,8 +388,16 @@
             case STATUS_IN_PROGRESS:
                 builder.setContentText(getString(R.string.notification_install_inprogress));
 
-                int max = (int) Math.max((mSystemSize + mUserdataSize) >> 20, 1);
-                int progress = (int) (mInstalledSize >> 20);
+                int max = 1024;
+                int progress = 0;
+
+                int currentMax = max >> (mNumInstalledPartitions + 1);
+                progress = max - currentMax * 2;
+
+                long currentProgress = (mCurrentPartitionInstalledSize >> 20) * currentMax
+                        / Math.max(mCurrentPartitionSize >> 20, 1);
+
+                progress += (int) currentProgress;
 
                 builder.setProgress(max, progress, false);
 
@@ -464,7 +480,8 @@
             throws RemoteException {
         Bundle bundle = new Bundle();
 
-        bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mInstalledSize);
+        // TODO: send more info to the clients
+        bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mCurrentPartitionInstalledSize);
 
         if (detail != null) {
             bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
@@ -492,9 +509,7 @@
                 return STATUS_IN_PROGRESS;
 
             case FINISHED:
-                int result = mInstallTask.getResult();
-
-                if (result == RESULT_OK) {
+                if (mInstallTask.isCompleted()) {
                     return STATUS_READY;
                 } else {
                     throw new IllegalStateException("A failed InstallationTask is not reset");
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 19ae970..b206a1f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -17,7 +17,6 @@
 package com.android.dynsystem;
 
 import android.content.Context;
-import android.gsi.GsiProgress;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.MemoryFile;
@@ -27,35 +26,70 @@
 import android.webkit.URLUtil;
 
 import java.io.BufferedInputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
 import java.util.Locale;
 import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
 
-class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
+class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Progress, Throwable> {
 
     private static final String TAG = "InstallationAsyncTask";
 
     private static final int READ_BUFFER_SIZE = 1 << 13;
+    private static final long MIN_PROGRESS_TO_PUBLISH = 1 << 27;
 
-    private class InvalidImageUrlException extends RuntimeException {
-        private InvalidImageUrlException(String message) {
+    private static final List<String> UNSUPPORTED_PARTITIONS =
+            Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
+
+    private class UnsupportedUrlException extends RuntimeException {
+        private UnsupportedUrlException(String message) {
             super(message);
         }
     }
 
-    /** Not completed, including being cancelled */
-    static final int NO_RESULT = 0;
+    private class UnsupportedFormatException extends RuntimeException {
+        private UnsupportedFormatException(String message) {
+            super(message);
+        }
+    }
+
+    /** UNSET means the installation is not completed */
+    static final int RESULT_UNSET = 0;
     static final int RESULT_OK = 1;
-    static final int RESULT_ERROR_IO = 2;
-    static final int RESULT_ERROR_INVALID_URL = 3;
+    static final int RESULT_CANCELLED = 2;
+    static final int RESULT_ERROR_IO = 3;
+    static final int RESULT_ERROR_UNSUPPORTED_URL = 4;
+    static final int RESULT_ERROR_UNSUPPORTED_FORMAT = 5;
     static final int RESULT_ERROR_EXCEPTION = 6;
 
-    interface InstallStatusListener {
-        void onProgressUpdate(long installedSize);
+    class Progress {
+        String mPartitionName;
+        long mPartitionSize;
+        long mInstalledSize;
+
+        int mNumInstalledPartitions;
+
+        Progress(String partitionName, long partitionSize, long installedSize,
+                int numInstalled) {
+            mPartitionName = partitionName;
+            mPartitionSize = partitionSize;
+            mInstalledSize = installedSize;
+
+            mNumInstalledPartitions = numInstalled;
+        }
+    }
+
+    interface ProgressListener {
+        void onProgressUpdate(Progress progress);
         void onResult(int resultCode, Throwable detail);
-        void onCancelled();
     }
 
     private final String mUrl;
@@ -63,16 +97,17 @@
     private final long mUserdataSize;
     private final Context mContext;
     private final DynamicSystemManager mDynSystem;
-    private final InstallStatusListener mListener;
+    private final ProgressListener mListener;
     private DynamicSystemManager.Session mInstallationSession;
 
-    private int mResult = NO_RESULT;
+    private boolean mIsZip;
+    private boolean mIsCompleted;
 
     private InputStream mStream;
-
+    private ZipFile mZipFile;
 
     InstallationAsyncTask(String url, long systemSize, long userdataSize, Context context,
-            DynamicSystemManager dynSystem, InstallStatusListener listener) {
+            DynamicSystemManager dynSystem, ProgressListener listener) {
         mUrl = url;
         mSystemSize = systemSize;
         mUserdataSize = userdataSize;
@@ -82,133 +117,292 @@
     }
 
     @Override
-    protected void onPreExecute() {
-        mListener.onProgressUpdate(0);
-    }
-
-    @Override
     protected Throwable doInBackground(String... voids) {
         Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
 
         try {
-            long installedSize = 0;
-            long reportedInstalledSize = 0;
+            // call DynamicSystemManager to cleanup stuff
+            mDynSystem.remove();
 
-            long minStepToReport = (mSystemSize + mUserdataSize) / 100;
+            verifyAndPrepare();
 
-            // init input stream before calling startInstallation(), which takes 90 seconds.
-            initInputStream();
+            mDynSystem.startInstallation();
 
-            Thread thread =
-                    new Thread(
-                            () -> {
-                                mDynSystem.startInstallation();
-                                mDynSystem.createPartition("userdata", mUserdataSize, false);
-                                mInstallationSession =
-                                        mDynSystem.createPartition("system", mSystemSize, true);
-                            });
-
-            thread.start();
-
-            while (thread.isAlive()) {
-                if (isCancelled()) {
-                    boolean aborted = mDynSystem.abort();
-                    Log.d(TAG, "Called DynamicSystemManager.abort(), result = " + aborted);
-                    return null;
-                }
-
-                GsiProgress progress = mDynSystem.getInstallationProgress();
-                installedSize = progress.bytes_processed;
-
-                if (installedSize > reportedInstalledSize + minStepToReport) {
-                    publishProgress(installedSize);
-                    reportedInstalledSize = installedSize;
-                }
-
-                Thread.sleep(10);
+            installUserdata();
+            if (isCancelled()) {
+                mDynSystem.remove();
+                return null;
             }
 
-            if (mInstallationSession == null) {
-                throw new IOException(
-                        "Failed to start installation with requested size: "
-                                + (mSystemSize + mUserdataSize));
+            installImages();
+            if (isCancelled()) {
+                mDynSystem.remove();
+                return null;
             }
 
-            installedSize = mUserdataSize;
-
-            MemoryFile memoryFile = new MemoryFile("dsu", READ_BUFFER_SIZE);
-            byte[] bytes = new byte[READ_BUFFER_SIZE];
-            mInstallationSession.setAshmem(
-                    new ParcelFileDescriptor(memoryFile.getFileDescriptor()), READ_BUFFER_SIZE);
-            int numBytesRead;
-            Log.d(TAG, "Start installation loop");
-            while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
-                memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
-                if (isCancelled()) {
-                    break;
-                }
-                if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
-                    throw new IOException("Failed write() to DynamicSystem");
-                }
-
-                installedSize += numBytesRead;
-
-                if (installedSize > reportedInstalledSize + minStepToReport) {
-                    publishProgress(installedSize);
-                    reportedInstalledSize = installedSize;
-                }
-            }
             mDynSystem.finishInstallation();
-            return null;
-
         } catch (Exception e) {
             e.printStackTrace();
+            mDynSystem.remove();
             return e;
         } finally {
             close();
         }
+
+        return null;
+    }
+
+    @Override
+    protected void onPostExecute(Throwable detail) {
+        int result = RESULT_UNSET;
+
+        if (detail == null) {
+            result = RESULT_OK;
+            mIsCompleted = true;
+        } else if (detail instanceof IOException) {
+            result = RESULT_ERROR_IO;
+        } else if (detail instanceof UnsupportedUrlException) {
+            result = RESULT_ERROR_UNSUPPORTED_URL;
+        } else if (detail instanceof UnsupportedFormatException) {
+            result = RESULT_ERROR_UNSUPPORTED_FORMAT;
+        } else {
+            result = RESULT_ERROR_EXCEPTION;
+        }
+
+        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+
+        mListener.onResult(result, detail);
     }
 
     @Override
     protected void onCancelled() {
         Log.d(TAG, "onCancelled(), URL: " + mUrl);
 
-        mListener.onCancelled();
-    }
-
-    @Override
-    protected void onPostExecute(Throwable detail) {
-        if (detail == null) {
-            mResult = RESULT_OK;
-        } else if (detail instanceof IOException) {
-            mResult = RESULT_ERROR_IO;
-        } else if (detail instanceof InvalidImageUrlException) {
-            mResult = RESULT_ERROR_INVALID_URL;
+        if (mDynSystem.abort()) {
+            Log.d(TAG, "Installation aborted");
         } else {
-            mResult = RESULT_ERROR_EXCEPTION;
+            Log.w(TAG, "DynamicSystemManager.abort() returned false");
         }
 
-        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + mResult);
-
-        mListener.onResult(mResult, detail);
+        mListener.onResult(RESULT_CANCELLED, null);
     }
 
     @Override
-    protected void onProgressUpdate(Long... values) {
-        long progress = values[0];
+    protected void onProgressUpdate(Progress... values) {
+        Progress progress = values[0];
         mListener.onProgressUpdate(progress);
     }
 
-    private void initInputStream() throws IOException, InvalidImageUrlException {
-        if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) {
-            mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream()));
-        } else if (URLUtil.isContentUrl(mUrl)) {
-            Uri uri = Uri.parse(mUrl);
-            mStream = new BufferedInputStream(new GZIPInputStream(
-                    mContext.getContentResolver().openInputStream(uri)));
+    private void verifyAndPrepare() throws Exception {
+        String extension = mUrl.substring(mUrl.lastIndexOf('.') + 1);
+
+        if ("gz".equals(extension) || "gzip".equals(extension)) {
+            mIsZip = false;
+        } else if ("zip".equals(extension)) {
+            mIsZip = true;
         } else {
-            throw new InvalidImageUrlException(
-                    String.format(Locale.US, "Unsupported file source: %s", mUrl));
+            throw new UnsupportedFormatException(
+                String.format(Locale.US, "Unsupported file format: %s", mUrl));
+        }
+
+        if (URLUtil.isNetworkUrl(mUrl)) {
+            mStream = new URL(mUrl).openStream();
+        } else if (URLUtil.isFileUrl(mUrl)) {
+            if (mIsZip) {
+                mZipFile = new ZipFile(new File(new URL(mUrl).toURI()));
+            } else {
+                mStream = new URL(mUrl).openStream();
+            }
+        } else if (URLUtil.isContentUrl(mUrl)) {
+            mStream = mContext.getContentResolver().openInputStream(Uri.parse(mUrl));
+        } else {
+            throw new UnsupportedUrlException(
+                    String.format(Locale.US, "Unsupported URL: %s", mUrl));
+        }
+    }
+
+    private void installUserdata() throws Exception {
+        Thread thread = new Thread(() -> {
+            mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
+        });
+
+        Log.d(TAG, "Creating partition: userdata");
+        thread.start();
+
+        long installedSize = 0;
+        Progress progress = new Progress("userdata", mUserdataSize, installedSize, 0);
+
+        while (thread.isAlive()) {
+            if (isCancelled()) {
+                return;
+            }
+
+            installedSize = mDynSystem.getInstallationProgress().bytes_processed;
+
+            if (installedSize > progress.mInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+                progress.mInstalledSize = installedSize;
+                publishProgress(progress);
+            }
+
+            Thread.sleep(10);
+        }
+
+        if (mInstallationSession == null) {
+            throw new IOException(
+                    "Failed to start installation with requested size: " + mUserdataSize);
+        }
+    }
+
+    private void installImages() throws IOException, InterruptedException {
+        if (mStream != null) {
+            if (mIsZip) {
+                installStreamingZipUpdate();
+            } else {
+                installStreamingGzUpdate();
+            }
+        } else {
+            installLocalZipUpdate();
+        }
+    }
+
+    private void installStreamingGzUpdate() throws IOException, InterruptedException {
+        Log.d(TAG, "To install a streaming GZ update");
+        installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
+    }
+
+    private void installStreamingZipUpdate() throws IOException, InterruptedException {
+        Log.d(TAG, "To install a streaming ZIP update");
+
+        ZipInputStream zis = new ZipInputStream(mStream);
+        ZipEntry zipEntry = null;
+
+        int numInstalledPartitions = 1;
+
+        while ((zipEntry = zis.getNextEntry()) != null) {
+            if (installImageFromAnEntry(zipEntry, zis, numInstalledPartitions)) {
+                numInstalledPartitions++;
+            }
+
+            if (isCancelled()) {
+                break;
+            }
+        }
+    }
+
+    private void installLocalZipUpdate() throws IOException, InterruptedException {
+        Log.d(TAG, "To install a local ZIP update");
+
+        Enumeration<? extends ZipEntry> entries = mZipFile.entries();
+        int numInstalledPartitions = 1;
+
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            if (installImageFromAnEntry(
+                    entry, mZipFile.getInputStream(entry), numInstalledPartitions)) {
+                numInstalledPartitions++;
+            }
+
+            if (isCancelled()) {
+                break;
+            }
+        }
+    }
+
+    private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
+            int numInstalledPartitions) throws IOException, InterruptedException {
+        String name = entry.getName();
+
+        Log.d(TAG, "ZipEntry: " + name);
+
+        if (!name.endsWith(".img")) {
+            return false;
+        }
+
+        String partitionName = name.substring(0, name.length() - 4);
+
+        if (UNSUPPORTED_PARTITIONS.contains(partitionName)) {
+            Log.d(TAG, name + " installation is not supported, skip it.");
+            return false;
+        }
+
+        long uncompressedSize = entry.getSize();
+
+        installImage(partitionName, uncompressedSize, is, numInstalledPartitions);
+
+        return true;
+    }
+
+    private void installImage(String partitionName, long uncompressedSize, InputStream is,
+            int numInstalledPartitions) throws IOException, InterruptedException {
+
+        SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
+
+        long unsparseSize = sis.getUnsparseSize();
+
+        final long partitionSize;
+
+        if (unsparseSize != -1) {
+            partitionSize = unsparseSize;
+            Log.d(TAG, partitionName + " is sparse, raw size = " + unsparseSize);
+        } else if (uncompressedSize != -1) {
+            partitionSize = uncompressedSize;
+            Log.d(TAG, partitionName + " is already unsparse, raw size = " + uncompressedSize);
+        } else {
+            throw new IOException("Cannot get raw size for " + partitionName);
+        }
+
+        Thread thread = new Thread(() -> {
+            mInstallationSession =
+                    mDynSystem.createPartition(partitionName, partitionSize, true);
+        });
+
+        Log.d(TAG, "Start creating partition: " + partitionName);
+        thread.start();
+
+        while (thread.isAlive()) {
+            if (isCancelled()) {
+                return;
+            }
+
+            Thread.sleep(10);
+        }
+
+        if (mInstallationSession == null) {
+            throw new IOException(
+                    "Failed to start installation with requested size: " + partitionSize);
+        }
+
+        Log.d(TAG, "Start installing: " + partitionName);
+
+        MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, READ_BUFFER_SIZE);
+        ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor());
+
+        mInstallationSession.setAshmem(pfd, READ_BUFFER_SIZE);
+
+        long installedSize = 0;
+        Progress progress = new Progress(
+                partitionName, partitionSize, installedSize, numInstalledPartitions);
+
+        byte[] bytes = new byte[READ_BUFFER_SIZE];
+        int numBytesRead;
+
+        while ((numBytesRead = sis.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+            if (isCancelled()) {
+                return;
+            }
+
+            memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
+
+            if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
+                throw new IOException("Failed write() to DynamicSystem");
+            }
+
+            installedSize += numBytesRead;
+
+            if (installedSize > progress.mInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+                progress.mInstalledSize = installedSize;
+                publishProgress(progress);
+            }
         }
     }
 
@@ -218,20 +412,20 @@
                 mStream.close();
                 mStream = null;
             }
+            if (mZipFile != null) {
+                mZipFile.close();
+                mZipFile = null;
+            }
         } catch (IOException e) {
             // ignore
         }
     }
 
-    int getResult() {
-        return mResult;
+    boolean isCompleted() {
+        return mIsCompleted;
     }
 
     boolean commit() {
-        if (mInstallationSession == null) {
-            return false;
-        }
-
-        return mInstallationSession.commit();
+        return mDynSystem.setEnable(true, true);
     }
 }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
new file mode 100644
index 0000000..72230b4
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -0,0 +1,199 @@
+/*
+ * 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.dynsystem;
+
+import static java.lang.Math.min;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * SparseInputStream read from upstream and detects the data format. If the upstream is a valid
+ * sparse data, it will unsparse it on the fly. Otherwise, it just passthrough as is.
+ */
+public class SparseInputStream extends InputStream {
+    static final int FILE_HDR_SIZE = 28;
+    static final int CHUNK_HDR_SIZE = 12;
+
+    /**
+     * This class represents a chunk in the Android sparse image.
+     *
+     * @see system/core/libsparse/sparse_format.h
+     */
+    private class SparseChunk {
+        static final short RAW = (short) 0xCAC1;
+        static final short FILL = (short) 0xCAC2;
+        static final short DONTCARE = (short) 0xCAC3;
+        public short mChunkType;
+        public int mChunkSize;
+        public int mTotalSize;
+        public byte[] fill;
+        public String toString() {
+            return String.format(
+                    "type: %x, chunk_size: %d, total_size: %d", mChunkType, mChunkSize, mTotalSize);
+        }
+    }
+
+    private byte[] readFull(InputStream in, int size) throws IOException {
+        byte[] buf = new byte[size];
+        for (int done = 0, n = 0; done < size; done += n) {
+            if ((n = in.read(buf, done, size - done)) < 0) {
+                throw new IOException("Failed to readFull");
+            }
+        }
+        return buf;
+    }
+
+    private ByteBuffer readBuffer(InputStream in, int size) throws IOException {
+        return ByteBuffer.wrap(readFull(in, size)).order(ByteOrder.LITTLE_ENDIAN);
+    }
+
+    private SparseChunk readChunk(InputStream in) throws IOException {
+        SparseChunk chunk = new SparseChunk();
+        ByteBuffer buf = readBuffer(in, CHUNK_HDR_SIZE);
+        chunk.mChunkType = buf.getShort();
+        buf.getShort();
+        chunk.mChunkSize = buf.getInt();
+        chunk.mTotalSize = buf.getInt();
+        return chunk;
+    }
+
+    private BufferedInputStream mIn;
+    private boolean mIsSparse;
+    private long mBlockSize;
+    private long mTotalBlocks;
+    private long mTotalChunks;
+    private SparseChunk mCur;
+    private long mLeft;
+    private int mCurChunks;
+
+    public SparseInputStream(BufferedInputStream in) throws IOException {
+        mIn = in;
+        in.mark(FILE_HDR_SIZE * 2);
+        ByteBuffer buf = readBuffer(mIn, FILE_HDR_SIZE);
+        mIsSparse = (buf.getInt() == 0xed26ff3a);
+        if (!mIsSparse) {
+            mIn.reset();
+            return;
+        }
+        int major = buf.getShort();
+        int minor = buf.getShort();
+
+        if (major > 0x1 || minor > 0x0) {
+            throw new IOException("Unsupported sparse version: " + major + "." + minor);
+        }
+
+        if (buf.getShort() != FILE_HDR_SIZE) {
+            throw new IOException("Illegal file header size");
+        }
+        if (buf.getShort() != CHUNK_HDR_SIZE) {
+            throw new IOException("Illegal chunk header size");
+        }
+        mBlockSize = buf.getInt();
+        if ((mBlockSize & 0x3) != 0) {
+            throw new IOException("Illegal block size, must be a multiple of 4");
+        }
+        mTotalBlocks = buf.getInt();
+        mTotalChunks = buf.getInt();
+        mLeft = mCurChunks = 0;
+    }
+
+    /**
+     * Check if it needs to open a new chunk.
+     *
+     * @return true if it's EOF
+     */
+    private boolean prepareChunk() throws IOException {
+        if (mCur == null || mLeft <= 0) {
+            if (++mCurChunks > mTotalChunks) return true;
+            mCur = readChunk(mIn);
+            if (mCur.mChunkType == SparseChunk.FILL) {
+                mCur.fill = readFull(mIn, 4);
+            }
+            mLeft = mCur.mChunkSize * mBlockSize;
+        }
+        return mLeft == 0;
+    }
+
+    /**
+     * It overrides the InputStream.read(byte[] buf)
+     */
+    public int read(byte[] buf) throws IOException {
+        if (!mIsSparse) {
+            return mIn.read(buf);
+        }
+        if (prepareChunk()) return -1;
+        int n = -1;
+        switch (mCur.mChunkType) {
+            case SparseChunk.RAW:
+                n = mIn.read(buf, 0, (int) min(mLeft, buf.length));
+                mLeft -= n;
+                return n;
+            case SparseChunk.DONTCARE:
+                n = (int) min(mLeft, buf.length);
+                Arrays.fill(buf, 0, n - 1, (byte) 0);
+                mLeft -= n;
+                return n;
+            case SparseChunk.FILL:
+                // The FILL type is rarely used, so use a simple implmentation.
+                return super.read(buf);
+            default:
+                throw new IOException("Unsupported Chunk:" + mCur.toString());
+        }
+    }
+
+    /**
+     * It overrides the InputStream.read()
+     */
+    public int read() throws IOException {
+        if (!mIsSparse) {
+            return mIn.read();
+        }
+        if (prepareChunk()) return -1;
+        int ret = -1;
+        switch (mCur.mChunkType) {
+            case SparseChunk.RAW:
+                ret = mIn.read();
+                break;
+            case SparseChunk.DONTCARE:
+                ret = 0;
+                break;
+            case SparseChunk.FILL:
+                ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3];
+                break;
+            default:
+                throw new IOException("Unsupported Chunk:" + mCur.toString());
+        }
+        mLeft--;
+        return ret;
+    }
+
+    /**
+     * Get the unsparse size
+     * @return -1 if unknown
+     */
+    public long getUnsparseSize() {
+        if (!mIsSparse) {
+            return -1;
+        }
+        return mBlockSize * mTotalBlocks;
+    }
+}
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
new file mode 100644
index 0000000..5254dde
--- /dev/null
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -0,0 +1,8 @@
+android_library {
+    name: "SettingsLibSearchProvider",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SearchProvider/AndroidManifest.xml b/packages/SettingsLib/SearchProvider/AndroidManifest.xml
new file mode 100644
index 0000000..2c06673
--- /dev/null
+++ b/packages/SettingsLib/SearchProvider/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.search">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java b/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java
new file mode 100644
index 0000000..0b711ec
--- /dev/null
+++ b/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java
@@ -0,0 +1,120 @@
+/*
+ * 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.settingslib.searchprovider;
+
+import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.SearchIndexableResource;
+import android.provider.SearchIndexablesContract.XmlResource;
+import android.provider.SearchIndexablesProvider;
+import android.text.TextUtils;
+
+import java.util.Collection;
+
+/**
+ * An abstract SearchIndexProvider using {@link SearchIndexableIntentResource} for indexing
+ */
+public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {
+    private static final String TAG = "XmlIndexProvider";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor queryXmlResources(String[] projection) {
+        final Context context = getContext();
+        final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
+        final Collection<SearchIndexableIntentResource> resources = getIntentResources();
+
+        for (SearchIndexableIntentResource indexableResource : resources) {
+            cursor.newRow()
+                    .add(XmlResource.COLUMN_RANK, indexableResource.rank)
+                    .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)
+                    .add(XmlResource.COLUMN_CLASS_NAME, indexableResource.className)
+                    .add(XmlResource.COLUMN_INTENT_ACTION, indexableResource.intentAction)
+                    .add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, context.getPackageName())
+                    .add(XmlResource.COLUMN_INTENT_TARGET_CLASS,
+                            indexableResource.intentTargetClass);
+        }
+        return cursor;
+    }
+
+    /**
+     * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+     *
+     * Those are the raw indexable data.
+     *
+     * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+     *                   to put into the cursor. If {@code null} all supported columns should be
+     *                   included.
+     */
+    public Cursor queryRawData(String[] projection) {
+        return null;
+    }
+
+    /**
+     * Returns all {@link android.provider.SearchIndexablesContract.NonIndexableKey}.
+     *
+     * Those are the non indexable data keys.
+     *
+     * @param projection list of {@link android.provider.SearchIndexablesContract.NonIndexableKey}
+     *                   columns to put into the cursor. If {@code null} all supported columns
+     *                   should be included.
+     */
+    public Cursor queryNonIndexableKeys(String[] projection) {
+        return null;
+    }
+
+    /**
+     * Returns a Collection of {@link SearchIndexableIntentResource} that should be indexed for
+     * search.
+     */
+    protected abstract Collection<SearchIndexableIntentResource> getIntentResources();
+
+    /**
+     * Wrapper class of {@link SearchIndexableResource}. It is for setting the search indexable
+     * resource of corresponding XML and intent action with class.
+     */
+    public static final class SearchIndexableIntentResource extends SearchIndexableResource {
+        /**
+         * Constructor of {@link SearchIndexableIntentResource}.
+         *
+         * @param xmlResId preference xml of target {@link prefereceFragment}
+         * @param intentAction the intent to open target {@link Activity}
+         * @param className the target {@link Activity} class name
+         */
+        public SearchIndexableIntentResource(int xmlResId, String intentAction,
+                String className) {
+            super(
+                    0 /* rank */,
+                    xmlResId,
+                    className,
+                    0 /* icon resource id */);
+            if (TextUtils.isEmpty(intentAction)) {
+                this.intentAction = "android.intent.action.MAIN";
+            } else {
+                this.intentAction = intentAction;
+            }
+            this.intentTargetClass = className;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index 5ac788e..3b41fa9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -32,7 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.DrawableWrapper;
 import android.os.Handler;
-import android.telephony.SignalStrength;
+import android.telephony.CellSignalStrength;
 import android.util.LayoutDirection;
 import android.util.PathParser;
 
@@ -145,7 +145,7 @@
 
     private int unpackLevel(int packedState) {
         int numBins = (packedState & NUM_LEVEL_MASK) >> NUM_LEVEL_SHIFT;
-        int levelOffset = numBins == (SignalStrength.NUM_SIGNAL_STRENGTH_BINS + 1) ? 10 : 0;
+        int levelOffset = numBins == (CellSignalStrength.getNumSignalStrengthLevels() + 1) ? 10 : 0;
         int level = (packedState & LEVEL_MASK);
         return level + levelOffset;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index f14def1..653c8ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -328,7 +328,6 @@
             boolean enabled, int rowId, Uri conditionId) {
         if (tag.lines == null) {
             tag.lines = row.findViewById(android.R.id.content);
-            tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
         }
         if (tag.line1 == null) {
             tag.line1 = (TextView) row.findViewById(android.R.id.text1);
@@ -364,6 +363,7 @@
             @Override
             public void onClick(View v) {
                 onClickTimeButton(row, tag, false /*down*/, rowId);
+                tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
             }
         });
 
@@ -373,6 +373,7 @@
             @Override
             public void onClick(View v) {
                 onClickTimeButton(row, tag, true /*up*/, rowId);
+                tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
             }
         });
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
index 66ee802..01a5789 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
@@ -234,6 +234,7 @@
             @Override
             public void onClick(View v) {
                 onClickTimeButton(row, tag, false /*down*/, rowIndex);
+                tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
             }
         });
 
@@ -243,6 +244,7 @@
             @Override
             public void onClick(View v) {
                 onClickTimeButton(row, tag, true /*up*/, rowIndex);
+                tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
             }
         });
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 7d06e6c..7b1c382 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -120,7 +120,7 @@
     private OsuProvider createOsuProvider() {
         Map<String, String> friendlyNames = new HashMap<>();
         friendlyNames.put("en", OSU_FRIENDLY_NAME);
-        return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null, null);
+        return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null);
     }
 
     @Before
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 94fbc54..81cf118 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -336,7 +336,7 @@
     private static OsuProvider buildOsuProvider(String friendlyName) {
         Map<String, String> friendlyNames = new HashMap<>();
         friendlyNames.put("en", friendlyName);
-        return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null, null);
+        return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null);
     }
 
     private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 05246a4..21b3ba3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -39,7 +39,6 @@
     <bool name="def_bluetooth_on">true</bool>
     <bool name="def_wifi_display_on">false</bool>
     <bool name="def_install_non_market_apps">false</bool>
-    <bool name="def_package_verifier_enable">true</bool>
     <!-- 0 == off, 3 == on -->
     <integer name="def_location_mode">3</integer>
     <bool name="assisted_gps_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java
new file mode 100644
index 0000000..5f3d7f1
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+import android.text.TextUtils;
+
+/**
+ * Ensure a restored value is a string in the format the accessibility shortcut system handles
+ *
+ * @hide
+ */
+public final class AccessibilityShortcutTargetListValidator extends ListValidator {
+    public AccessibilityShortcutTargetListValidator() {
+        super(":");
+    }
+
+    @Override
+    protected boolean isEntryValid(String entry) {
+        return !TextUtils.isEmpty(entry);
+    }
+
+    @Override
+    protected boolean isItemValid(String item) {
+        if (TextUtils.isEmpty(item)) {
+            return false;
+        }
+        return (COMPONENT_NAME_VALIDATOR.validate(item) || PACKAGE_NAME_VALIDATOR.validate(item));
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
index a6001d2..104fa11 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
@@ -35,7 +35,7 @@
         if (!isEntryValid(value)) {
             return false;
         }
-        String[] items = value.split(",");
+        String[] items = value.split(mListSplitRegex);
         for (String item : items) {
             if (!isItemValid(item)) {
                 return false;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9460d27f..090af98 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -16,6 +16,7 @@
 
 package android.provider.settings.validators;
 
+import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
@@ -71,10 +72,13 @@
         VALIDATORS.put(Secure.TOUCH_EXPLORATION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
-                Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
+                Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
         // technically either ComponentName or class name, but there's proper value
         // validation at callsites, so allow any non-null string
-        VALIDATORS.put(Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, value -> value != null);
+        VALIDATORS.put(
+                Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 224042c..71c7544 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -204,4 +204,7 @@
             new InclusiveIntegerRangeValidator(0, 100);
 
     static final Validator VIBRATION_INTENSITY_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3);
+
+    static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
+            new AccessibilityShortcutTargetListValidator();
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0c4db49..2027345 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1191,19 +1191,7 @@
         }
 
         if (upgradeVersion == 81) {
-            // Add package verification setting
-            db.beginTransaction();
-            SQLiteStatement stmt = null;
-            try {
-                stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
-                        + " VALUES(?,?);");
-                loadBooleanSetting(stmt, Settings.Global.PACKAGE_VERIFIER_ENABLE,
-                        R.bool.def_package_verifier_enable);
-                db.setTransactionSuccessful();
-            } finally {
-                db.endTransaction();
-                if (stmt != null) stmt.close();
-            }
+            // package_verifier_enable has been removed
             upgradeVersion = 82;
         }
 
@@ -1305,7 +1293,6 @@
                 db.beginTransaction();
                 try {
                     String[] settingsToMove = {
-                            Settings.Global.PACKAGE_VERIFIER_ENABLE,
                             Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
                             Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE
                     };
@@ -2470,9 +2457,6 @@
             loadDefaultAnimationSettings(stmt);
 
             // --- Previously in 'secure'
-            loadBooleanSetting(stmt, Settings.Global.PACKAGE_VERIFIER_ENABLE,
-                    R.bool.def_package_verifier_enable);
-
             loadBooleanSetting(stmt, Settings.Global.WIFI_ON,
                     R.bool.def_wifi_on);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 33e0fd2..0a2dd38 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1162,9 +1162,6 @@
 
         final long pkgVerifierToken = p.start(GlobalSettingsProto.PACKAGE_VERIFIER);
         dumpSetting(s, p,
-                Settings.Global.PACKAGE_VERIFIER_ENABLE,
-                GlobalSettingsProto.PackageVerifier.ENABLED);
-        dumpSetting(s, p,
                 Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
                 GlobalSettingsProto.PackageVerifier.TIMEOUT);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 0959de9..93a1407 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -967,11 +967,6 @@
                     final long identity = Binder.clearCallingIdentity();
                     try {
                         synchronized (mLock) {
-                            Setting enable = getGlobalSetting(
-                                    Settings.Global.PACKAGE_VERIFIER_ENABLE);
-                            String enableValue = enable != null ? enable.getValue() : null;
-                            updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
-                                    enableValue, null, true, userId, true);
                             Setting include = getGlobalSetting(
                                     Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
                             String includeValue = include != null ? include.getValue() : null;
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index eefba5d..540726b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -387,7 +387,6 @@
                     Settings.Global.OVERLAY_DISPLAY_DEVICES,
                     Settings.Global.PAC_CHANGE_DELAY,
                     Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
-                    Settings.Global.PACKAGE_VERIFIER_ENABLE,
                     Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
                     Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE,
                     Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index a3b0835..bb9e6f6 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -297,6 +297,18 @@
     }
 
     @Test
+    public void testAccessibilityShortcutTargetValidator() {
+        assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+                "com.google.android/.AccessibilityShortcutTarget"));
+        assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+                "com.google.android"));
+        assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+                "com.google.android/.AccessibilityShortcutTarget:com.google.android"));
+        assertFalse(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+                "com.google.android/.AccessibilityShortcutTarget:com.google.@android"));
+    }
+
+    @Test
     public void ensureAllBackedUpGlobalSettingsHaveValidators() {
         String offenders = getOffenders(concat(GlobalSettings.SETTINGS_TO_BACKUP,
                 Settings.Global.LEGACY_RESTORE_SETTINGS), GlobalSettingsValidators.VALIDATORS);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e9c20db..0a671d9 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -206,6 +206,9 @@
     <!-- shortcut manager -->
     <uses-permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING" />
 
+    <!-- launcher apps -->
+    <uses-permission android:name="android.permission.ACCESS_SHORTCUTS" />
+
     <uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" />
 
     <!-- accessibility -->
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 281406f..26ec572 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -15,6 +15,9 @@
             "include-filter": "android.platform.test.scenario.notification"
         },
         {
+            "include-annotation": "android.platform.test.scenario.annotation.Scenario"
+        },
+        {
             "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 1bfc4c0..b90a371 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -24,7 +24,25 @@
     android:outlineProvider="none"
     android:elevation="5dp" > <!-- Put it above the status bar header -->
 
-    <include layout="@layout/keyguard_indication_area_overlay" />
+    <LinearLayout
+        android:id="@+id/keyguard_indication_area"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
+        android:layout_gravity="bottom|center_horizontal"
+        android:orientation="vertical">
+
+        <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+            android:id="@+id/keyguard_indication_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:paddingStart="@dimen/keyguard_indication_text_padding"
+            android:paddingEnd="@dimen/keyguard_indication_text_padding"
+            android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+            android:accessibilityLiveRegion="polite"/>
+
+    </LinearLayout>
 
     <FrameLayout
         android:id="@+id/preview_container"
diff --git a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml b/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
deleted file mode 100644
index cc30a68..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/keyguard_indication_area"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
-    android:layout_gravity="bottom|center_horizontal"
-    android:orientation="vertical">
-
-  <include layout="@layout/keyguard_indication_text_view" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
deleted file mode 100644
index 9376b1f..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
-        android:id="@+id/keyguard_indication_text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:paddingStart="@dimen/keyguard_indication_text_padding"
-        android:paddingEnd="@dimen/keyguard_indication_text_padding"
-        android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
-        android:accessibilityLiveRegion="polite"/>
-</merge>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 09f512f..7cce1ba 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -52,7 +52,7 @@
             android:scrollIndicators="top"
             android:scrollbars="vertical"
             android:scrollbarStyle="outsideOverlay"
-            android:importantForAccessibility="no" />
+            android:importantForAccessibility="auto" />
     </com.android.keyguard.AlphaOptimizedLinearLayout>
 
     <View
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index a1f0ee7..da80633 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -53,12 +53,12 @@
             android:src="@drawable/ic_settings"
             android:visibility="gone"/>
 
-        <Switch
-            android:id="@android:id/toggle"
+        <ViewStub
+            android:id="@+id/toggle_stub"
+            android:inflatedId="@+id/toggle"
+            android:layout="@layout/qs_detail_switch"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:clickable="false"
-            android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+            android:layout_height="wrap_content"/>
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/qs_detail_switch.xml b/packages/SystemUI/res/layout/qs_detail_switch.xml
new file mode 100644
index 0000000..abb2497
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail_switch.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+
+<Switch
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/toggle"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:clickable="false"
+    android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
new file mode 100644
index 0000000..0669357
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_media_panel_options.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/qs_media_controls_options"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:gravity="center"
+    android:layout_gravity="center"
+    android:padding="10dp"
+    >
+    <ImageButton
+        android:id="@+id/remove"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:src="@android:drawable/ic_menu_delete"
+        android:padding="8dp"
+    />
+    <ImageButton
+        android:id="@+id/cancel"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:src="@android:drawable/ic_menu_revert"
+        android:padding="8dp"
+    />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f7b92b5..1a05ec1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1179,4 +1179,5 @@
     <dimen name="qs_media_height">150dp</dimen>
     <dimen name="qs_media_width">350dp</dimen>
     <dimen name="qs_media_padding">8dp</dimen>
+    <dimen name="qs_media_corner_radius">10dp</dimen>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index fe5a57a..8c0ffb8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -83,16 +83,16 @@
     }
 
     @Override
-    public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+    public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
         for (PinnedStackListener listener : mListeners) {
-            listener.onSaveReentrySnapFraction(componentName, bounds);
+            listener.onSaveReentryBounds(componentName, bounds);
         }
     }
 
     @Override
-    public void onResetReentrySnapFraction(ComponentName componentName) {
+    public void onResetReentryBounds(ComponentName componentName) {
         for (PinnedStackListener listener : mListeners) {
-            listener.onResetReentrySnapFraction(componentName);
+            listener.onResetReentryBounds(componentName);
         }
     }
 
@@ -140,9 +140,9 @@
 
         public void onActionsChanged(ParceledListSlice actions) {}
 
-        public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {}
+        public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {}
 
-        public void onResetReentrySnapFraction(ComponentName componentName) {}
+        public void onResetReentryBounds(ComponentName componentName) {}
 
         public void onDisplayInfoChanged(DisplayInfo displayInfo) {}
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1d4b9ef..9bba2aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -151,7 +151,6 @@
     private static final int MSG_DPM_STATE_CHANGED = 309;
     private static final int MSG_USER_SWITCHING = 310;
     private static final int MSG_KEYGUARD_RESET = 312;
-    private static final int MSG_BOOT_COMPLETED = 313;
     private static final int MSG_USER_SWITCH_COMPLETE = 314;
     private static final int MSG_USER_INFO_CHANGED = 317;
     private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
@@ -234,7 +233,6 @@
     private boolean mGoingToSleep;
     private boolean mBouncer;
     private boolean mAuthInterruptActive;
-    private boolean mBootCompleted;
     private boolean mNeedsSlowUnlockTransition;
     private boolean mHasLockscreenWallpaper;
     private boolean mAssistantVisible;
@@ -1075,8 +1073,6 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
-            } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                dispatchBootCompleted();
             } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
@@ -1550,9 +1546,6 @@
                     case MSG_KEYGUARD_BOUNCER_CHANGED:
                         handleKeyguardBouncerChanged(msg.arg1);
                         break;
-                    case MSG_BOOT_COMPLETED:
-                        handleBootCompleted();
-                        break;
                     case MSG_USER_INFO_CHANGED:
                         handleUserInfoChanged(msg.arg1);
                         break;
@@ -1648,11 +1641,6 @@
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mHandler);
 
-        final IntentFilter bootCompleteFilter = new IntentFilter();
-        bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
-        broadcastDispatcher.registerReceiver(mBroadcastReceiver, bootCompleteFilter, mHandler);
-
         final IntentFilter allUserFilter = new IntentFilter();
         allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
@@ -2103,39 +2091,6 @@
     }
 
     /**
-     * This is exposed since {@link Intent#ACTION_BOOT_COMPLETED} is not sticky. If
-     * keyguard crashes sometime after boot, then it will never receive this
-     * broadcast and hence not handle the event. This method is ultimately called by
-     * PhoneWindowManager in this case.
-     */
-    public void dispatchBootCompleted() {
-        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
-    }
-
-    /**
-     * Handle {@link #MSG_BOOT_COMPLETED}
-     */
-    private void handleBootCompleted() {
-        checkIsHandlerThread();
-        if (mBootCompleted) return;
-        mBootCompleted = true;
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onBootCompleted();
-            }
-        }
-    }
-
-    /**
-     * We need to store this state in the KeyguardUpdateMonitor since this class will not be
-     * destroyed.
-     */
-    public boolean hasBootCompleted() {
-        return mBootCompleted;
-    }
-
-    /**
      * Handle {@link #MSG_DEVICE_PROVISIONED}
      */
     private void handleDeviceProvisioned() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index b4b83d6..04502f0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -148,14 +148,6 @@
     public void onUserUnlocked() { }
 
     /**
-     * Called when boot completed.
-     *
-     * Note, this callback will only be received if boot complete occurs after registering with
-     * KeyguardUpdateMonitor.
-     */
-    public void onBootCompleted() { }
-
-    /**
      * Called when the emergency call button is pressed.
      */
     public void onEmergencyCallAction() { }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 9793d72..8e49d58 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -33,6 +33,7 @@
 import androidx.slice.Clock;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.systemui.BootCompleteCache;
 import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -83,7 +84,6 @@
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
-            Intent.ACTION_BOOT_COMPLETED,
             Intent.ACTION_PACKAGE_ADDED,
             Intent.ACTION_PACKAGE_CHANGED,
             Intent.ACTION_PACKAGE_REMOVED
@@ -150,6 +150,15 @@
             mDefaultHome = getCurrentDefaultHome();
         }
     };
+
+    private final BootCompleteCache.BootCompleteListener mBootCompleteListener =
+            new BootCompleteCache.BootCompleteListener() {
+        @Override
+        public void onBootComplete() {
+            mDefaultHome = getCurrentDefaultHome();
+        }
+    };
+
     private final IntentFilter mDefaultHomeIntentFilter;
     private final Runnable mResetConsecutiveTaskSwitches = this::resetConsecutiveTaskSwitches;
 
@@ -163,6 +172,7 @@
     private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
     private final Lazy<PackageManagerWrapper> mPackageManagerWrapper;
     private final Lazy<BroadcastDispatcher> mBroadcastDispatcher;
+    private final Lazy<BootCompleteCache> mBootCompleteCache;
 
     private boolean mOnLockscreen;
     private boolean mIsDozing;
@@ -196,7 +206,8 @@
             Lazy<SysUiState> sysUiFlagContainer,
             Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
             Lazy<PackageManagerWrapper> packageManagerWrapper,
-            Lazy<BroadcastDispatcher> broadcastDispatcher) {
+            Lazy<BroadcastDispatcher> broadcastDispatcher,
+            Lazy<BootCompleteCache> bootCompleteCache) {
         mClock = clock;
         mHandler = handler;
         mDeviceConfigHelper = deviceConfigHelper;
@@ -211,6 +222,7 @@
             mDefaultHomeIntentFilter.addAction(action);
         }
         mBroadcastDispatcher = broadcastDispatcher;
+        mBootCompleteCache = bootCompleteCache;
     }
 
     @Override
@@ -218,6 +230,7 @@
         mContext = context;
         mAssistHandleCallbacks = callbacks;
         mConsecutiveTaskSwitches = 0;
+        mBootCompleteCache.get().addListener(mBootCompleteListener);
         mDefaultHome = getCurrentDefaultHome();
         mBroadcastDispatcher.get()
                 .registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
@@ -250,6 +263,7 @@
         mAssistHandleCallbacks = null;
         if (mContext != null) {
             mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver);
+            mBootCompleteCache.get().removeListener(mBootCompleteListener);
             Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
             Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
             Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 8cccffa..9de6854 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.systemui.BootCompleteCache;
 import com.android.systemui.Dependency;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -62,7 +63,6 @@
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
-            Intent.ACTION_BOOT_COMPLETED,
             Intent.ACTION_PACKAGE_ADDED,
             Intent.ACTION_PACKAGE_CHANGED,
             Intent.ACTION_PACKAGE_REMOVED
@@ -77,13 +77,14 @@
 
     @Inject
     PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
-            Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+            Optional<Lazy<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
         mContext = context;
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
 
         ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
         mDefaultHome = getCurrentDefaultHome();
+        bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
         IntentFilter intentFilter = new IntentFilter();
         for (String action : DEFAULT_HOME_CHANGE_ACTIONS) {
             intentFilter.addAction(action);
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 776189b..adb288a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -56,7 +56,7 @@
  * a given broadcast.
  *
  * Use only for IntentFilters with actions and optionally categories. It does not support,
- * permissions, schemes, data types or data authorities.
+ * permissions, schemes, data types, data authorities or priority different than 0.
  * Cannot be used for getting sticky broadcasts.
  */
 @Singleton
@@ -104,6 +104,7 @@
         if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ")
         if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ")
         if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ")
+        if (filter.priority != 0) sb.append("Filter cannot modify priority. ")
         if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString())
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index c6b9090..044ea54 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -27,9 +27,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -57,6 +61,7 @@
     private final String mGroupId;
     private String mAppName;
     private Drawable mUserBadgedAppIcon;
+    private ShortcutInfo mShortcutInfo;
 
     private boolean mInflated;
     private BubbleView mIconView;
@@ -94,6 +99,14 @@
         mLastUpdated = e.getSbn().getPostTime();
         mGroupId = groupId(e);
 
+        String shortcutId = e.getSbn().getNotification().getShortcutId();
+        if (BubbleExperimentConfig.useShortcutInfoToBubble(context)
+                && shortcutId != null) {
+            mShortcutInfo = BubbleExperimentConfig.getShortcutInfo(context,
+                    e.getSbn().getPackageName(),
+                    e.getSbn().getUser(), shortcutId);
+        }
+
         PackageManager pm = context.getPackageManager();
         ApplicationInfo info;
         try {
@@ -137,6 +150,21 @@
         return mUserBadgedAppIcon;
     }
 
+    @Nullable
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    /**
+     * Whether shortcut information should be used to populate the bubble.
+     * <p>
+     * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
+     * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
+     */
+    public boolean usingShortcutInfo() {
+        return BubbleExperimentConfig.isShortcutIntent(getBubbleIntent());
+    }
+
     boolean isInflated() {
         return mInflated;
     }
@@ -331,7 +359,7 @@
     }
 
     @Nullable
-    PendingIntent getBubbleIntent(Context context) {
+    PendingIntent getBubbleIntent() {
         Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
         if (data != null) {
             return data.getIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index e9c19d2..ea51f4a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -130,12 +130,17 @@
                             Log.d(TAG, "onActivityViewReady: calling startActivity, "
                                     + "bubble=" + getBubbleKey());
                         }
-                        Intent fillInIntent = new Intent();
-                        // Apply flags to make behaviour match documentLaunchMode=always.
-                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
-                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                         try {
-                            mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
+                            if (mBubble.usingShortcutInfo()) {
+                                mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
+                                        options, null /* sourceBounds */);
+                            } else {
+                                Intent fillInIntent = new Intent();
+                                // Apply flags to make behaviour match documentLaunchMode=always.
+                                fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                                fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                                mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
+                            }
                         } catch (RuntimeException e) {
                             // If there's a runtime exception here then there's something
                             // wrong with the intent, we can't really recover / try to populate
@@ -415,7 +420,7 @@
                     + getBubbleKey());
         }
 
-        mBubbleIntent = mBubble.getBubbleIntent(mContext);
+        mBubbleIntent = mBubble.getBubbleIntent();
         if (mBubbleIntent != null) {
             setContentVisibility(false);
             mActivityView.setVisibility(VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index b478a72..6eeb5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -16,32 +16,58 @@
 
 package com.android.systemui.bubbles;
 
+import static android.app.Notification.EXTRA_MESSAGES;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+
+import static com.android.systemui.bubbles.BubbleController.canLaunchIntentInActivityView;
+
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.UserHandle;
 import android.provider.Settings;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Common class for experiments controlled via secure settings.
  */
 public class BubbleExperimentConfig {
 
+    private static final String SHORTCUT_DUMMY_INTENT = "bubble_experiment_shortcut_intent";
+    private static PendingIntent sDummyShortcutIntent;
+
+    private static final int BUBBLE_HEIGHT = 10000;
+
     private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble";
     private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
 
     private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
     private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
 
+    private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
+    private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
+
     /**
      * When true, if a notification has the information necessary to bubble (i.e. valid
      * contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
      * object will be created by the system and added to the notification.
-     *
-     * This does not produce a bubble, only adds the metadata. It should be used in conjunction
-     * with {@see #allowNotifBubbleMenu} which shows an affordance to bubble notification content.
+     * <p>
+     * This does not produce a bubble, only adds the metadata based on the notification info.
      */
     static boolean allowAnyNotifToBubble(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
@@ -60,16 +86,28 @@
     }
 
     /**
+     * When true, if the notification is able to bubble via {@link #allowAnyNotifToBubble(Context)}
+     * or {@link #allowMessageNotifsToBubble(Context)} or via normal BubbleMetadata, then a new
+     * BubbleMetadata object is constructed based on the shortcut info.
+     * <p>
+     * This does not produce a bubble, only adds the metadata based on shortcut info.
+     */
+    static boolean useShortcutInfoToBubble(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ALLOW_SHORTCUTS_TO_BUBBLE,
+                ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT ? 1 : 0) != 0;
+    }
+
+    /**
      * If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
      * {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
      * the notification has necessary info for BubbleMetadata.
      */
     static void adjustForExperiments(Context context, NotificationEntry entry,
             Bubble previousBubble) {
-        if (entry.getBubbleMetadata() != null) {
-            // Has metadata, nothing to do.
-            return;
-        }
+
+        Notification.BubbleMetadata metadata = null;
+        boolean addedMetadata = false;
 
         Notification notification = entry.getSbn().getNotification();
         boolean isMessage = Notification.MessagingStyle.class.equals(
@@ -77,22 +115,131 @@
         boolean bubbleNotifForExperiment = (isMessage && allowMessageNotifsToBubble(context))
                 || allowAnyNotifToBubble(context);
 
-        final PendingIntent intent = notification.contentIntent;
-        if (bubbleNotifForExperiment
-                && BubbleController.canLaunchIntentInActivityView(context, entry, intent)) {
-            final Icon smallIcon = entry.getSbn().getNotification().getSmallIcon();
-            Notification.BubbleMetadata.Builder metadata =
-                    new Notification.BubbleMetadata.Builder()
-                            .setDesiredHeight(10000)
-                            .setIcon(smallIcon)
-                            .setIntent(intent);
-            entry.setBubbleMetadata(metadata.build());
+        boolean useShortcutInfo = useShortcutInfoToBubble(context);
+        String shortcutId = entry.getSbn().getNotification().getShortcutId();
+
+        if (useShortcutInfo && shortcutId != null) {
+            // We don't actually get anything useful from ShortcutInfo so just check existence
+            ShortcutInfo info = getShortcutInfo(context, entry.getSbn().getPackageName(),
+                    entry.getSbn().getUser(), shortcutId);
+            if (info != null) {
+                metadata = createForShortcut(context, entry);
+            }
+
+            // Replace existing metadata with shortcut, or we're bubbling for experiment
+            boolean shouldBubble = entry.getBubbleMetadata() != null || bubbleNotifForExperiment;
+
+            if (shouldBubble && metadata != null) {
+                entry.setBubbleMetadata(metadata);
+                addedMetadata = true;
+            }
         }
 
-        if (previousBubble != null) {
-            // Update to a previously user-created bubble, set its flag now so the update goes
+        // Didn't get metadata from a shortcut & we're bubbling for experiment
+        if (entry.getBubbleMetadata() == null && bubbleNotifForExperiment) {
+            metadata = createFromNotif(context, entry);
+            if (metadata != null) {
+                entry.setBubbleMetadata(metadata);
+                addedMetadata = true;
+            }
+        }
+
+        if (previousBubble != null && addedMetadata) {
+            // Update to a previously bubble, set its flag now so the update goes
             // to the bubble.
             entry.setFlagBubble(true);
         }
     }
+
+    static Notification.BubbleMetadata createFromNotif(Context context, NotificationEntry entry) {
+        Notification notification = entry.getSbn().getNotification();
+        final PendingIntent intent = notification.contentIntent;
+        Icon icon = null;
+        // Use the icon of the person if available
+        List<Person> personList = getPeopleFromNotification(entry);
+        if (personList.size() > 0) {
+            icon = personList.get(0).getIcon();
+        }
+        if (icon == null) {
+            icon = notification.getLargeIcon() != null
+                    ? notification.getLargeIcon()
+                    : notification.getSmallIcon();
+        }
+        if (canLaunchIntentInActivityView(context, entry, intent)) {
+            return new Notification.BubbleMetadata.Builder()
+                    .setDesiredHeight(BUBBLE_HEIGHT)
+                    .setIcon(icon)
+                    .setIntent(intent)
+                    .build();
+        }
+        return null;
+    }
+
+    static Notification.BubbleMetadata createForShortcut(Context context, NotificationEntry entry) {
+        // ShortcutInfo does not return an icon, instead a Drawable, lets just use
+        // notification icon for BubbleMetadata.
+        Icon icon = entry.getSbn().getNotification().getSmallIcon();
+
+        // ShortcutInfo does not return the intent, lets make a fake but identifiable
+        // intent so we can still add bubbleMetadata
+        if (sDummyShortcutIntent == null) {
+            Intent i = new Intent(SHORTCUT_DUMMY_INTENT);
+            sDummyShortcutIntent = PendingIntent.getActivity(context, 0, i,
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+        }
+        return new Notification.BubbleMetadata.Builder()
+                .setDesiredHeight(BUBBLE_HEIGHT)
+                .setIcon(icon)
+                .setIntent(sDummyShortcutIntent)
+                .build();
+    }
+
+    static ShortcutInfo getShortcutInfo(Context context, String packageName, UserHandle user,
+            String shortcutId) {
+        LauncherApps launcherAppService =
+                (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+        if (packageName != null) {
+            query.setPackage(packageName);
+        }
+        if (shortcutId != null) {
+            query.setShortcutIds(Arrays.asList(shortcutId));
+        }
+        query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST);
+        List<ShortcutInfo> shortcuts = launcherAppService.getShortcuts(query, user);
+        return shortcuts != null && shortcuts.size() > 0
+                ? shortcuts.get(0)
+                : null;
+    }
+
+    static boolean isShortcutIntent(PendingIntent intent) {
+        return intent.equals(sDummyShortcutIntent);
+    }
+
+    static List<Person> getPeopleFromNotification(NotificationEntry entry) {
+        Bundle extras = entry.getSbn().getNotification().extras;
+        ArrayList<Person> personList = new ArrayList<>();
+        if (extras == null) {
+            return personList;
+        }
+
+        List<Person> p = extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST);
+
+        if (p != null) {
+            personList.addAll(p);
+        }
+
+        if (Notification.MessagingStyle.class.equals(
+                entry.getSbn().getNotification().getNotificationStyle())) {
+            final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+            if (!ArrayUtils.isEmpty(messages)) {
+                for (Notification.MessagingStyle.Message message :
+                        Notification.MessagingStyle.Message
+                                .getMessagesFromBundleArray(messages)) {
+                    personList.add(message.getSenderPerson());
+                }
+            }
+        }
+        return personList;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 35657d3..79807b3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -243,9 +244,16 @@
     }
 
     Drawable getBubbleDrawable(Context context) {
-        Notification.BubbleMetadata metadata = getEntry().getBubbleMetadata();
-        Icon ic = metadata.getIcon();
-        return ic.loadDrawable(context);
+        if (mBubble.getShortcutInfo() != null && mBubble.usingShortcutInfo()) {
+            LauncherApps launcherApps =
+                    (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+            int density = getContext().getResources().getConfiguration().densityDpi;
+            return launcherApps.getShortcutIconDrawable(mBubble.getShortcutInfo(), density);
+        } else {
+            Notification.BubbleMetadata metadata = getEntry().getBubbleMetadata();
+            Icon ic = metadata.getIcon();
+            return ic.loadDrawable(context);
+        }
     }
 
     BitmapInfo getBadgedBitmap() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f026e68..8d08b28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2082,7 +2082,6 @@
 
     @Override
     public void onBootCompleted() {
-        mUpdateMonitor.dispatchBootCompleted();
         synchronized (this) {
             mBootCompleted = true;
             if (mBootSendUserPresent) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 686e7db..f10274a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -64,6 +64,7 @@
     private IPinnedStackController mPinnedStackController;
     private ComponentName mLastPipComponentName;
     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
+    private Size mReentrySize = null;
 
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
@@ -162,7 +163,7 @@
     public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
             Rect animatingBounds, DisplayInfo displayInfo) {
         getInsetBounds(insetBounds);
-        final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
+        final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null);
         normalBounds.set(defaultBounds);
         if (animatingBounds.isEmpty()) {
             animatingBounds.set(defaultBounds);
@@ -175,26 +176,28 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on saving reentry snap fraction
+     * Responds to IPinnedStackListener on saving reentry snap fraction and size
      * for a given {@link ComponentName}.
      */
-    public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+    public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
         mReentrySnapFraction = getSnapFraction(bounds);
+        mReentrySize = new Size(bounds.width(), bounds.height());
         mLastPipComponentName = componentName;
     }
 
     /**
-     * Responds to IPinnedStackListener on resetting reentry snap fraction
+     * Responds to IPinnedStackListener on resetting reentry snap fraction and size
      * for a given {@link ComponentName}.
      */
-    public void onResetReentrySnapFraction(ComponentName componentName) {
+    public void onResetReentryBounds(ComponentName componentName) {
         if (componentName.equals(mLastPipComponentName)) {
-            onResetReentrySnapFractionUnchecked();
+            onResetReentryBoundsUnchecked();
         }
     }
 
-    private void onResetReentrySnapFractionUnchecked() {
+    private void onResetReentryBoundsUnchecked() {
         mReentrySnapFraction = INVALID_SNAP_FRACTION;
+        mReentrySize = null;
         mLastPipComponentName = null;
     }
 
@@ -233,7 +236,7 @@
     public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
         final Rect destinationBounds;
         if (bounds == null) {
-            destinationBounds = getDefaultBounds(mReentrySnapFraction);
+            destinationBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
         } else {
             destinationBounds = new Rect(bounds);
         }
@@ -245,7 +248,7 @@
             return;
         }
         mAspectRatio = aspectRatio;
-        onResetReentrySnapFractionUnchecked();
+        onResetReentryBoundsUnchecked();
         try {
             mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
                     -1 /* animationDuration */);
@@ -269,13 +272,14 @@
      */
     private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
             boolean useCurrentMinEdgeSize) {
-        // Save the snap fraction, calculate the aspect ratio based on screen size
+
+        // Save the snap fraction and adjust the size based on the new aspect ratio.
         final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                 getMovementBounds(stackBounds));
-
         final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
-        final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
-                mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        final Size size = mSnapAlgorithm.getSizeForAspectRatio(
+                new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+
         final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
@@ -286,21 +290,20 @@
     }
 
     /**
-     * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
-     * will apply the default bounds to the provided snap fraction.
+     * @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
+     * provided, then it will apply the default bounds to the provided snap fraction and size.
      */
-    private Rect getDefaultBounds(float snapFraction) {
-        final Rect insetBounds = new Rect();
-        getInsetBounds(insetBounds);
-
+    private Rect getDefaultBounds(float snapFraction, Size size) {
         final Rect defaultBounds = new Rect();
-        final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
-                mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-        if (snapFraction != INVALID_SNAP_FRACTION) {
+        if (snapFraction != INVALID_SNAP_FRACTION && size != null) {
             defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
             final Rect movementBounds = getMovementBounds(defaultBounds);
             mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
         } else {
+            final Rect insetBounds = new Rect();
+            getInsetBounds(insetBounds);
+            size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+                    mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
                     0, Math.max(mIsImeShowing ? mImeHeight : 0,
                             mIsShelfShowing ? mShelfHeight : 0),
@@ -364,11 +367,19 @@
      * @return the default snap fraction to apply instead of the default gravity when calculating
      *         the default stack bounds when first entering PiP.
      */
-    private float getSnapFraction(Rect stackBounds) {
+    public float getSnapFraction(Rect stackBounds) {
         return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
     }
 
     /**
+     * Applies the given snap fraction to the given stack bounds.
+     */
+    public void applySnapFraction(Rect stackBounds, float snapFraction) {
+        final Rect movementBounds = getMovementBounds(stackBounds);
+        mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
+    }
+
+    /**
      * @return the pixels for a given dp value.
      */
     private int dpToPx(float dpValue, DisplayMetrics dm) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index c33b8d9..a4707cf 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -65,6 +65,7 @@
     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
     private final Rect mTmpInsetBounds = new Rect();
     private final Rect mTmpNormalBounds = new Rect();
+    private final Rect mReentryBounds = new Rect();
 
     private PipBoundsHandler mPipBoundsHandler;
     private InputConsumerController mInputConsumerController;
@@ -164,13 +165,25 @@
         }
 
         @Override
-        public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
-            mHandler.post(() -> mPipBoundsHandler.onSaveReentrySnapFraction(componentName, bounds));
+        public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
+            mHandler.post(() -> {
+                // On phones, the expansion animation that happens on pip tap before restoring
+                // to fullscreen makes it so that the bounds received here are the expanded
+                // bounds. We want to restore to the unexpanded bounds when re-entering pip,
+                // so we save the bounds before expansion (normal) instead of the current
+                // bounds.
+                mReentryBounds.set(mTouchHandler.getNormalBounds());
+                // Apply the snap fraction of the current bounds to the normal bounds.
+                float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
+                mPipBoundsHandler.applySnapFraction(mReentryBounds, snapFraction);
+                // Save reentry bounds (normal non-expand bounds with current position applied).
+                mPipBoundsHandler.onSaveReentryBounds(componentName, mReentryBounds);
+            });
         }
 
         @Override
-        public void onResetReentrySnapFraction(ComponentName componentName) {
-            mHandler.post(() -> mPipBoundsHandler.onResetReentrySnapFraction(componentName));
+        public void onResetReentryBounds(ComponentName componentName) {
+            mHandler.post(() -> mPipBoundsHandler.onResetReentryBounds(componentName));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index f59b372..2e90a3e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -592,6 +592,13 @@
     }
 
     /**
+     * @return the unexpanded bounds.
+     */
+    public Rect getNormalBounds() {
+        return mNormalBounds;
+    }
+
+    /**
      * Gesture controlling normal movement of the PIP.
      */
     private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 4982dd4..eb19571 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -476,7 +476,7 @@
                 });
         d.setOnDismissListener(dialogInterface -> {
             mUsbHighTempDialog = null;
-            Events.writeEvent(mContext, Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
+            Events.writeEvent(Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
                     Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED,
                     mKeyguard.isKeyguardLocked());
         });
@@ -485,7 +485,7 @@
         d.show();
         mUsbHighTempDialog = d;
 
-        Events.writeEvent(mContext, Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
+        Events.writeEvent(Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
                 Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED,
                 mKeyguard.isKeyguardLocked());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 60d30da..019cb14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -28,6 +28,7 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -58,7 +59,8 @@
 
     protected View mQsDetailHeader;
     protected TextView mQsDetailHeaderTitle;
-    protected Switch mQsDetailHeaderSwitch;
+    private ViewStub mQsDetailHeaderSwitchStub;
+    private Switch mQsDetailHeaderSwitch;
     protected ImageView mQsDetailHeaderProgress;
 
     protected QSTileHost mHost;
@@ -98,7 +100,7 @@
 
         mQsDetailHeader = findViewById(R.id.qs_detail_header);
         mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
-        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
+        mQsDetailHeaderSwitchStub = mQsDetailHeader.findViewById(R.id.toggle_stub);
         mQsDetailHeaderProgress = findViewById(R.id.qs_detail_header_progress);
 
         updateDetailText();
@@ -252,9 +254,12 @@
         mQsDetailHeaderTitle.setText(adapter.getTitle());
         final Boolean toggleState = adapter.getToggleState();
         if (toggleState == null) {
-            mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
+            if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
             mQsDetailHeader.setClickable(false);
         } else {
+            if (mQsDetailHeaderSwitch == null) {
+                mQsDetailHeaderSwitch = (Switch) mQsDetailHeaderSwitchStub.inflate();
+            }
             mQsDetailHeaderSwitch.setVisibility(VISIBLE);
             handleToggleStateChanged(toggleState, adapter.getToggleEnabled());
             mQsDetailHeader.setClickable(true);
@@ -274,9 +279,9 @@
         if (mAnimatingOpen) {
             return;
         }
-        mQsDetailHeaderSwitch.setChecked(state);
+        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setChecked(state);
         mQsDetailHeader.setEnabled(toggleEnabled);
-        mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
+        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
     }
 
     private void handleScanStateChanged(boolean state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 1a4c327..f7e4c79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -18,8 +18,11 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -35,6 +38,7 @@
 import android.media.session.PlaybackState;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -54,6 +58,8 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 
+import java.util.List;
+
 /**
  * Single media player for carousel in QSPanel
  */
@@ -70,6 +76,83 @@
     private int mHeight;
     private int mForegroundColor;
     private int mBackgroundColor;
+    private ComponentName mRecvComponent;
+    private QSPanel mParent;
+
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+        @Override
+        public void onSessionDestroyed() {
+            Log.d(TAG, "session destroyed");
+            mController.unregisterCallback(mSessionCallback);
+
+            // Hide all the old buttons
+            final int[] actionIds = {
+                    R.id.action0,
+                    R.id.action1,
+                    R.id.action2,
+                    R.id.action3,
+                    R.id.action4
+            };
+            for (int i = 0; i < actionIds.length; i++) {
+                ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+                if (thisBtn != null) {
+                    thisBtn.setVisibility(View.GONE);
+                }
+            }
+
+            // Add a restart button
+            ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+            btn.setOnClickListener(v -> {
+                Log.d(TAG, "Attempting to restart session");
+                // Send a media button event to previously found receiver
+                if (mRecvComponent != null) {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    intent.setComponent(mRecvComponent);
+                    int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+                    intent.putExtra(
+                            Intent.EXTRA_KEY_EVENT,
+                            new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+                    mContext.sendBroadcast(intent);
+                } else {
+                    Log.d(TAG, "No receiver to restart");
+                    // If we don't have a receiver, try relaunching the activity instead
+                    try {
+                        mController.getSessionActivity().send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Pending intent was canceled");
+                        e.printStackTrace();
+                    }
+                }
+            });
+            btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+            btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+            btn.setVisibility(View.VISIBLE);
+
+            // Add long-click option to remove the player
+            ViewGroup mMediaCarousel = (ViewGroup) mMediaNotifView.getParent();
+            mMediaNotifView.setOnLongClickListener(v -> {
+                // Replace player view with delete/cancel view
+                v.setVisibility(View.GONE);
+
+                View options = LayoutInflater.from(mContext).inflate(
+                        R.layout.qs_media_panel_options, null, false);
+                ImageButton btnDelete = options.findViewById(R.id.remove);
+                btnDelete.setOnClickListener(b -> {
+                    mMediaCarousel.removeView(options);
+                    mParent.removeMediaPlayer(QSMediaPlayer.this);
+                });
+                ImageButton btnCancel = options.findViewById(R.id.cancel);
+                btnCancel.setOnClickListener(b -> {
+                    mMediaCarousel.removeView(options);
+                    v.setVisibility(View.VISIBLE);
+                });
+
+                int pos = mMediaCarousel.indexOfChild(v);
+                mMediaCarousel.addView(options, pos, v.getLayoutParams());
+                return true; // consumed click
+            });
+        }
+    };
 
     /**
      *
@@ -92,7 +175,8 @@
     }
 
     /**
-     *
+     * Create or update the player view for the given media session
+     * @param parent the parent QSPanel
      * @param token token for this media session
      * @param icon app notification icon
      * @param iconColor foreground color (for text, icons)
@@ -101,13 +185,30 @@
      * @param notif reference to original notification
      * @param device current playback device
      */
-    public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
-            View actionsContainer, Notification notif, MediaDevice device) {
-        Log.d(TAG, "got media session: " + token);
+    public void setMediaSession(QSPanel parent, MediaSession.Token token, Icon icon, int iconColor,
+            int bgColor, View actionsContainer, Notification notif, MediaDevice device) {
+        mParent = parent;
         mToken = token;
         mForegroundColor = iconColor;
         mBackgroundColor = bgColor;
         mController = new MediaController(mContext, token);
+
+        // Try to find a receiver for the media button that matches this app
+        PackageManager pm = mContext.getPackageManager();
+        Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+        if (info != null) {
+            for (ResolveInfo inf : info) {
+                if (inf.activityInfo.packageName.equals(notif.contentIntent.getCreatorPackage())) {
+                    Log.d(TAG, "Found receiver for package: " + inf);
+                    mRecvComponent = inf.getComponentInfo().getComponentName();
+                }
+            }
+        }
+
+        // reset in case we had previously restarted the stream
+        mMediaNotifView.setOnLongClickListener(null);
+        mController.registerCallback(mSessionCallback);
         MediaMetadata mMediaMetadata = mController.getMetadata();
         if (mMediaMetadata == null) {
             Log.e(TAG, "Media metadata was null");
@@ -235,7 +336,6 @@
         for (; i < actionIds.length; i++) {
             ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
             thisBtn.setVisibility(View.GONE);
-            Log.d(TAG, "hid a button");
         }
     }
 
@@ -266,8 +366,9 @@
 
     private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
         Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+        float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
         if (albumArt != null) {
-
+            Log.d(TAG, "updating album art");
             Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
             Bitmap scaled = scaleBitmap(original, width, height);
             Canvas canvas = new Canvas(scaled);
@@ -281,12 +382,15 @@
 
             RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
                     mContext.getResources(), scaled);
-            roundedDrawable.setCornerRadius(20);
+            roundedDrawable.setCornerRadius(radius);
 
             mMediaNotifView.setBackground(roundedDrawable);
         } else {
             Log.e(TAG, "No album art available");
-            mMediaNotifView.setBackground(null);
+            GradientDrawable rect = new GradientDrawable();
+            rect.setCornerRadius(radius);
+            rect.setColor(bgColor);
+            mMediaNotifView.setBackground(rect);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5e98f93..51e352b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -284,7 +284,7 @@
         }
 
         Log.d(TAG, "setting player session");
-        player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
+        player.setMediaSession(this, token, icon, iconColor, bgColor, actionsContainer,
                 notif.getNotification(), mDevice);
 
         if (mMediaPlayers.size() > 0) {
@@ -303,6 +303,27 @@
         return mMediaCarousel;
     }
 
+    /**
+     * Remove the media player from the carousel
+     * @param player Player to remove
+     * @return true if removed, false if player was not found
+     */
+    protected boolean removeMediaPlayer(QSMediaPlayer player) {
+        // Remove from list
+        if (!mMediaPlayers.remove(player)) {
+            return false;
+        }
+
+        // Check if we need to collapse the carousel now
+        mMediaCarousel.removeView(player.getView());
+        if (mMediaPlayers.size() == 0) {
+            ((View) mMediaCarousel.getParent()).setVisibility(View.GONE);
+            mLocalMediaManager.stopScan();
+            mLocalMediaManager.unregisterCallback(mDeviceCallback);
+        }
+        return true;
+    }
+
     protected void addDivider() {
         mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
         mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index d7b8b83..5bb882e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -16,19 +16,27 @@
 
 package com.android.systemui.qs;
 
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -42,6 +50,8 @@
 
 import com.android.systemui.R;
 
+import java.util.List;
+
 /**
  * QQS mini media player
  */
@@ -53,6 +63,54 @@
     private LinearLayout mMediaNotifView;
     private MediaSession.Token mToken;
     private MediaController mController;
+    private int mBackgroundColor;
+    private int mForegroundColor;
+    private ComponentName mRecvComponent;
+
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+        @Override
+        public void onSessionDestroyed() {
+            Log.d(TAG, "session destroyed");
+            mController.unregisterCallback(mSessionCallback);
+
+            // Hide all the old buttons
+            final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
+            for (int i = 0; i < actionIds.length; i++) {
+                ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+                if (thisBtn != null) {
+                    thisBtn.setVisibility(View.GONE);
+                }
+            }
+
+            // Add a restart button
+            ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+            btn.setOnClickListener(v -> {
+                Log.d(TAG, "Attempting to restart session");
+                // Send a media button event to previously found receiver
+                if (mRecvComponent != null) {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    intent.setComponent(mRecvComponent);
+                    int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+                    intent.putExtra(
+                            Intent.EXTRA_KEY_EVENT,
+                            new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+                    mContext.sendBroadcast(intent);
+                } else {
+                    Log.d(TAG, "No receiver to restart");
+                    // If we don't have a receiver, try relaunching the activity instead
+                    try {
+                        mController.getSessionActivity().send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Pending intent was canceled");
+                        e.printStackTrace();
+                    }
+                }
+            });
+            btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+            btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+            btn.setVisibility(View.VISIBLE);
+        }
+    };
 
     /**
      *
@@ -83,34 +141,50 @@
             View actionsContainer, int[] actionsToShow) {
         Log.d(TAG, "Setting media session: " + token);
         mToken = token;
+        mForegroundColor = iconColor;
+        mBackgroundColor = bgColor;
         mController = new MediaController(mContext, token);
         MediaMetadata mMediaMetadata = mController.getMetadata();
 
+        // Try to find a receiver for the media button that matches this app
+        PackageManager pm = mContext.getPackageManager();
+        Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+        if (info != null) {
+            for (ResolveInfo inf : info) {
+                if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
+                    Log.d(TAG, "Found receiver for package: " + inf);
+                    mRecvComponent = inf.getComponentInfo().getComponentName();
+                }
+            }
+        }
+        mController.registerCallback(mSessionCallback);
+
         if (mMediaMetadata == null) {
             Log.e(TAG, "Media metadata was null");
             return;
         }
 
         // Album art
-        addAlbumArtBackground(mMediaMetadata, bgColor);
+        addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
 
         // App icon
         ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
         Drawable iconDrawable = icon.loadDrawable(mContext);
-        iconDrawable.setTint(iconColor);
+        iconDrawable.setTint(mForegroundColor);
         appIcon.setImageDrawable(iconDrawable);
 
         // Artist name
         TextView appText = mMediaNotifView.findViewById(R.id.header_title);
         String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
         appText.setText(artistName);
-        appText.setTextColor(iconColor);
+        appText.setTextColor(mForegroundColor);
 
         // Song name
         TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
         String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
         titleText.setText(songName);
-        titleText.setTextColor(iconColor);
+        titleText.setTextColor(mForegroundColor);
 
         // Buttons we can display
         final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
@@ -178,6 +252,7 @@
 
     private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
         Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+        float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
         if (albumArt != null) {
             Rect bounds = new Rect();
             mMediaNotifView.getBoundsOnScreen(bounds);
@@ -197,12 +272,15 @@
 
             RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
                     mContext.getResources(), scaled);
-            roundedDrawable.setCornerRadius(20);
+            roundedDrawable.setCornerRadius(radius);
 
             mMediaNotifView.setBackground(roundedDrawable);
         } else {
             Log.e(TAG, "No album art available");
-            mMediaNotifView.setBackground(null);
+            GradientDrawable rect = new GradientDrawable();
+            rect.setCornerRadius(radius);
+            rect.setColor(bgColor);
+            mMediaNotifView.setBackground(rect);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 20e3cee..47cb45b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -49,4 +49,9 @@
     protected boolean animationsEnabled() {
         return false;
     }
+
+    @Override
+    public boolean isLongClickable() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index ff34be0..1de6355 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -32,6 +32,7 @@
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
 
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -118,7 +119,13 @@
         mTileQueryHelper.setListener(mTileAdapter);
         mRecyclerView.setAdapter(mTileAdapter);
         mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
-        GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
+        GridLayoutManager layout = new GridLayoutManager(getContext(), 3) {
+            @Override
+            public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+                    RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
+                // Do not read row and column every time it changes.
+            }
+        };
         layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
         mRecyclerView.setLayoutManager(layout);
         mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index bd3297b..3afc460 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -238,9 +238,21 @@
         return true;
     }
 
+    private void setSelectableForHeaders(View view) {
+        if (mAccessibilityManager.isTouchExplorationEnabled()) {
+            final boolean selectable = mAccessibilityAction == ACTION_NONE;
+            view.setFocusable(selectable);
+            view.setImportantForAccessibility(selectable
+                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            view.setFocusableInTouchMode(selectable);
+        }
+    }
+
     @Override
     public void onBindViewHolder(final Holder holder, int position) {
         if (holder.getItemViewType() == TYPE_HEADER) {
+            setSelectableForHeaders(holder.itemView);
             return;
         }
         if (holder.getItemViewType() == TYPE_DIVIDER) {
@@ -260,6 +272,8 @@
             }
 
             ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText);
+            setSelectableForHeaders(holder.itemView);
+
             return;
         }
         if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -306,6 +320,7 @@
             holder.mTileView.setImportantForAccessibility(selectable
                     ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
                     : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            holder.mTileView.setFocusableInTouchMode(selectable);
             if (selectable) {
                 holder.mTileView.setOnClickListener(new OnClickListener() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index dc9a2ce..9fe9703 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -22,6 +22,7 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
+import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.R;
@@ -112,6 +113,7 @@
             state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         }
         state.showRippleEffect = false;
+        state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 5a5f9e9..9dd7f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -174,7 +174,7 @@
             if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
                 startAppDetailsSettingsActivity(pkg, uid, null, row);
             } else {
-                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION);
                 intent.setData(Uri.fromParts("package", pkg, null));
                 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 2a4b315..352ba0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -179,21 +179,22 @@
         if (Utils.useQsMediaPlayer(mContext)) {
             final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras
                     .getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+            int tintColor = getNotificationHeader().getOriginalIconColor();
             StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
             QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
                     com.android.systemui.R.id.quick_qs_panel);
             panel.getMediaPlayer().setMediaSession(token,
                     mRow.getEntry().getSbn().getNotification().getSmallIcon(),
-                    getNotificationHeader().getOriginalIconColor(),
-                    mRow.getCurrentBackgroundTint(),
+                    tintColor,
+                    mBackgroundColor,
                     mActions,
                     compactActions);
             QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
                     com.android.systemui.R.id.quick_settings_panel);
             bigPanel.addMediaSession(token,
                     mRow.getEntry().getSbn().getNotification().getSmallIcon(),
-                    getNotificationHeader().getOriginalIconColor(),
-                    mRow.getCurrentBackgroundTint(),
+                    tintColor,
+                    mBackgroundColor,
                     mActions,
                     mRow.getEntry().getSbn());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
index 20de5bb..f1b9590 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
@@ -43,6 +43,9 @@
 
     public int add(final TextView text, final int labelResId) {
         if (text == null) return 0;
+        if (mTexts.containsKey(text)) {
+            return mTexts.get(text);
+        }
         final Resources res = mContext.getResources();
         final float fontScale = res.getConfiguration().fontScale;
         final float density = res.getDisplayMetrics().density;
@@ -63,6 +66,11 @@
         return sp;
     }
 
+    public void remove(final TextView text) {
+        mTexts.remove(text);
+        mTextLabels.remove(text);
+    }
+
     public void update() {
         if (mTexts.isEmpty()) return;
         mTexts.keyAt(0).post(mUpdateAll);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 9bbfd22..5ed8b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.volume;
 
-import android.content.Context;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.provider.Settings.Global;
@@ -125,7 +124,12 @@
 
     public static Callback sCallback;
 
-    public static void writeEvent(Context context, int tag, Object... list) {
+    /**
+     * Logs an event to the system log and the event log.
+     * @param tag One of the EVENT_* codes above.
+     * @param list Any additional event-specific arguments, documented above.
+     */
+    public static void writeEvent(int tag, Object... list) {
         MetricsLogger logger = new MetricsLogger();
         final long time = System.currentTimeMillis();
         final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
@@ -133,33 +137,33 @@
             sb.append(" ");
             switch (tag) {
                 case EVENT_SHOW_DIALOG:
-                    MetricsLogger.visible(context, MetricsEvent.VOLUME_DIALOG);
-                    MetricsLogger.histogram(context, "volume_from_keyguard",
+                    logger.visible(MetricsEvent.VOLUME_DIALOG);
+                    logger.histogram("volume_from_keyguard",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
                     break;
                 case EVENT_EXPAND:
-                    MetricsLogger.visibility(context, MetricsEvent.VOLUME_DIALOG_DETAILS,
+                    logger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS,
                             (Boolean) list[0]);
                     sb.append(list[0]);
                     break;
                 case EVENT_DISMISS_DIALOG:
-                    MetricsLogger.hidden(context, MetricsEvent.VOLUME_DIALOG);
+                    logger.hidden(MetricsEvent.VOLUME_DIALOG);
                     sb.append(DISMISS_REASONS[(Integer) list[0]]);
                     break;
                 case EVENT_ACTIVE_STREAM_CHANGED:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_STREAM,
+                    logger.action(MetricsEvent.ACTION_VOLUME_STREAM,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0]));
                     break;
                 case EVENT_ICON_CLICK:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_ICON,
+                    logger.action(MetricsEvent.ACTION_VOLUME_ICON,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(iconStateToString((Integer) list[1]));
                     break;
                 case EVENT_TOUCH_LEVEL_DONE:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_SLIDER,
+                    logger.action(MetricsEvent.ACTION_VOLUME_SLIDER,
                             (Integer) list[1]);
                     // fall through
                 case EVENT_TOUCH_LEVEL_CHANGED:
@@ -169,7 +173,7 @@
                             .append(list[1]);
                     break;
                 case EVENT_KEY:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_KEY,
+                    logger.action(MetricsEvent.ACTION_VOLUME_KEY,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(list[1]);
@@ -181,7 +185,7 @@
                     logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
                     break;
                 case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_RINGER_MODE,
+                    logger.action(MetricsEvent.ACTION_RINGER_MODE,
                             (Integer) list[0]);
                     // fall through
                 case EVENT_INTERNAL_RINGER_MODE_CHANGED:
@@ -194,14 +198,14 @@
                     sb.append(list[0]).append(' ').append(list[1]);
                     break;
                 case EVENT_SHOW_USB_OVERHEAT_ALARM:
-                    MetricsLogger.visible(context, MetricsEvent.POWER_OVERHEAT_ALARM);
-                    MetricsLogger.histogram(context, "show_usb_overheat_alarm",
+                    logger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
+                    logger.histogram("show_usb_overheat_alarm",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
                     break;
                 case EVENT_DISMISS_USB_OVERHEAT_ALARM:
-                    MetricsLogger.hidden(context, MetricsEvent.POWER_OVERHEAT_ALARM);
-                    MetricsLogger.histogram(context, "dismiss_usb_overheat_alarm",
+                    logger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
+                    logger.histogram("dismiss_usb_overheat_alarm",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(DISMISS_REASONS[(Integer) list[0]])
                         .append(" keyguard=").append(list[1]);
@@ -255,4 +259,5 @@
         void writeEvent(long time, int tag, Object[] list);
         void writeState(long time, State state);
     }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 02c699f..a4ed31d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -150,7 +150,7 @@
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
-        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
+        Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
         mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName());
         mWorkerThread.start();
         mWorker = new W(mWorkerThread.getLooper());
@@ -237,7 +237,7 @@
         if (D.BUG) Log.d(TAG, "destroy");
         if (mDestroyed) return;
         mDestroyed = true;
-        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STOPPED);
+        Events.writeEvent(Events.EVENT_COLLECTION_STOPPED);
         mMediaSessions.destroy();
         mObserver.destroy();
         mReceiver.destroy();
@@ -487,7 +487,7 @@
             mCallbacks.onShowSilentHint();
         }
         if (changed && fromKey) {
-            Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
+            Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);
         }
         return changed;
     }
@@ -495,7 +495,7 @@
     private boolean updateActiveStreamW(int activeStream) {
         if (activeStream == mState.activeStream) return false;
         mState.activeStream = activeStream;
-        Events.writeEvent(mContext, Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
+        Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
         if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
         final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
         if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
@@ -544,7 +544,7 @@
         if (ss.level == level) return false;
         ss.level = level;
         if (isLogWorthy(stream)) {
-            Events.writeEvent(mContext, Events.EVENT_LEVEL_CHANGED, stream, level);
+            Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
         }
         return true;
     }
@@ -567,7 +567,7 @@
         if (ss.muted == muted) return false;
         ss.muted = muted;
         if (isLogWorthy(stream)) {
-            Events.writeEvent(mContext, Events.EVENT_MUTE_CHANGED, stream, muted);
+            Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
         }
         if (muted && isRinger(stream)) {
             updateRingerModeInternalW(mAudio.getRingerModeInternal());
@@ -583,7 +583,7 @@
         if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
         mState.effectsSuppressor = effectsSuppressor;
         mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
-        Events.writeEvent(mContext, Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
+        Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
                 mState.effectsSuppressorName);
         return true;
     }
@@ -607,7 +607,7 @@
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
         if (mState.zenMode == zen) return false;
         mState.zenMode = zen;
-        Events.writeEvent(mContext, Events.EVENT_ZEN_MODE_CHANGED, zen);
+        Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
         return true;
     }
 
@@ -633,23 +633,23 @@
         mState.disallowMedia = disallowMedia;
         mState.disallowSystem = disallowSystem;
         mState.disallowRinger = disallowRinger;
-        Events.writeEvent(mContext, Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms=" +
-                disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem=" +
-                disallowSystem + " disallowRinger=" + disallowRinger);
+        Events.writeEvent(Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms="
+                + disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem="
+                + disallowSystem + " disallowRinger=" + disallowRinger);
         return true;
     }
 
     private boolean updateRingerModeExternalW(int rm) {
         if (rm == mState.ringerModeExternal) return false;
         mState.ringerModeExternal = rm;
-        Events.writeEvent(mContext, Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
+        Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
         return true;
     }
 
     private boolean updateRingerModeInternalW(int rm) {
         if (rm == mState.ringerModeInternal) return false;
         mState.ringerModeInternal = rm;
-        Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+        Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
 
         if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
             playTouchFeedback();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 24d6c4c..f5c1587 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -429,7 +429,7 @@
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
             row.icon.setOnClickListener(v -> {
-                Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+                Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
                 mController.setActiveStream(row.stream);
                 if (row.stream == AudioManager.STREAM_RING) {
                     final boolean hasVibrator = mController.hasVibrator();
@@ -468,7 +468,7 @@
         }
         if (mSettingsIcon != null) {
             mSettingsIcon.setOnClickListener(v -> {
-                Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
+                Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
                 Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
                 Dependency.get(ActivityStarter.class).startActivity(intent,
@@ -504,7 +504,7 @@
                         mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                     }
                 }
-                Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
+                Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
                 incrementManualToggleCount();
                 updateRingerH();
                 provideTouchFeedbackH(newRingerMode);
@@ -519,7 +519,7 @@
         if (mODICaptionsIcon != null) {
             mODICaptionsIcon.setOnConfirmedTapListener(() -> {
                 onCaptionIconClicked();
-                Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK);
+                Events.writeEvent(Events.EVENT_ODI_CAPTIONS_CLICK);
             }, mHandler);
         }
 
@@ -541,7 +541,7 @@
             mODICaptionsTooltipView = mODICaptionsTooltipViewStub.inflate();
             mODICaptionsTooltipView.findViewById(R.id.dismiss).setOnClickListener(v -> {
                 hideCaptionsTooltip();
-                Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
+                Events.writeEvent(Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
             });
             mODICaptionsTooltipViewStub = null;
             rescheduleTimeoutH();
@@ -694,7 +694,7 @@
         initSettingsH();
         mShowing = true;
         mDialog.show();
-        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
         mController.notifyVisible(true);
         mController.getCaptionsComponentState(false);
         checkODICaptionsTooltip(false);
@@ -741,7 +741,7 @@
         if (mShowing) {
             mShowing = false;
             // Only logs when the volume dialog visibility is changed.
-            Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+            Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
         }
         mDialogView.setTranslationX(0);
         mDialogView.setAlpha(1);
@@ -922,6 +922,7 @@
             if (!mDynamic.get(row.stream)) {
                 mRows.remove(i);
                 mDialogRowsView.removeView(row.view);
+                mConfigurableTexts.remove(row.header);
             }
         }
     }
@@ -1400,7 +1401,7 @@
                     mController.setActiveStream(mRow.stream);
                     mController.setStreamVolume(mRow.stream, userLevel);
                     mRow.requestedLevel = userLevel;
-                    Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
+                    Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
                             userLevel);
                 }
             }
@@ -1419,7 +1420,7 @@
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
             final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
-            Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
+            Events.writeEvent(Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
             if (mRow.ss.level != userLevel) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
                         USER_ATTEMPT_GRACE_PERIOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index ead14e5..2242c1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -178,6 +178,14 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
     }
 
+    @Test(expected = IllegalArgumentException::class)
+    fun testFilterMustNotSetPriority() {
+        val testFilter = IntentFilter(TEST_ACTION).apply {
+            priority = IntentFilter.SYSTEM_HIGH_PRIORITY
+        }
+        broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+    }
+
     private class TestBroadcastDispatcher(
         context: Context,
         mainHandler: Handler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index ba28879..27e3a66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -64,6 +64,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
+@Ignore
 public class NotificationContentInflaterTest extends SysuiTestCase {
 
     private NotificationContentInflater mNotificationInflater;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 4b3249d..43d39a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -274,7 +274,7 @@
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mNotificationActivityStarter, times(1))
                 .startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, captor.getValue().getAction());
+        assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.getValue().getAction());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 2068f7c..dfd9941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -125,6 +126,7 @@
         assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.getKey()));
     }
 
+    @Ignore("b/141538055")
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
         NotificationEntry laterEntry = new NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 57dcbf2..4103d71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -26,6 +26,7 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
 import android.os.Looper;
+import android.telephony.CellSignalStrength;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
@@ -180,7 +181,7 @@
 
     @Test
     public void testCdmaSignalRoaming() {
-        for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        for (int testStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                 testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
             setupDefaultSignal();
             setCdma();
@@ -203,7 +204,7 @@
 
     @Test
     public void testQsSignalStrength() {
-        for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        for (int testStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                 testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
             setupDefaultSignal();
             setLevel(testStrength);
@@ -216,7 +217,7 @@
 
     @Test
     public void testCdmaQsSignalStrength() {
-        for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        for (int testStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                 testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
             setupDefaultSignal();
             setCdma();
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml
new file mode 100644
index 0000000..1258805
--- /dev/null
+++ b/packages/Tethering/res/values-af/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Verbinding of Wi-Fi-warmkol aktief"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om op te stel."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Verbinding is gedeaktiveer"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontak jou administrateur vir besonderhede"</string>
+</resources>
diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml
new file mode 100644
index 0000000..9c36192
--- /dev/null
+++ b/packages/Tethering/res/values-am/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ለማዋቀር መታ ያድርጉ።"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml
new file mode 100644
index 0000000..9f84ce4
--- /dev/null
+++ b/packages/Tethering/res/values-ar/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"النطاق أو نقطة الاتصال نشطة"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"انقر للإعداد."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"تم إيقاف التوصيل"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"اتصل بالمشرف للحصول على التفاصيل"</string>
+</resources>
diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml
new file mode 100644
index 0000000..8855822
--- /dev/null
+++ b/packages/Tethering/res/values-as/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ছেট আপ কৰিবলৈ টিপক।"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
+</resources>
diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml
new file mode 100644
index 0000000..eba50eb
--- /dev/null
+++ b/packages/Tethering/res/values-az/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tezerinq və ya hotspot aktivdir"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Quraşdırmaq üçün tıklayın."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Birləşmə deaktivdir"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Məlumat üçün adminlə əlaqə saxlayın"</string>
+</resources>
diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..5b0e488
--- /dev/null
+++ b/packages/Tethering/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste podesili."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Privezivanje je onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Potražite detalje od administratora"</string>
+</resources>
diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml
new file mode 100644
index 0000000..5966c71
--- /dev/null
+++ b/packages/Tethering/res/values-be/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Дакраніцеся, каб наладзіць."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Рэжым мадэма адключаны"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml
new file mode 100644
index 0000000..ed58d73
--- /dev/null
+++ b/packages/Tethering/res/values-bg/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Има активна споделена връзка или безжична точка за достъп"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Докоснете, за да настроите."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Функцията за тетъринг е деактивирана"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Свържете се с администратора си за подробности"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml
new file mode 100644
index 0000000..8d9880a
--- /dev/null
+++ b/packages/Tethering/res/values-bn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"টিথারিং বা হটস্পট সক্রিয় আছে"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"সেট-আপ করার জন্য আলতো চাপুন৷"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"টিথারিং অক্ষম করা আছে"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml
new file mode 100644
index 0000000..2361b9d
--- /dev/null
+++ b/packages/Tethering/res/values-bs/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite za postavke"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezivanje putem mobitela je onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontaktirajte svog administratora za dodatne detalje"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml
new file mode 100644
index 0000000..6752b51
--- /dev/null
+++ b/packages/Tethering/res/values-ca/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Toca per configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"La compartició de xarxa està desactivada"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta amb el teu administrador per obtenir més informació"</string>
+</resources>
diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml
new file mode 100644
index 0000000..5fdd53a
--- /dev/null
+++ b/packages/Tethering/res/values-cs/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Sdílené připojení nebo hotspot je aktivní."</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím zahájíte nastavení."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je zakázán"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požádejte administrátora"</string>
+</resources>
diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml
new file mode 100644
index 0000000..2775dfa
--- /dev/null
+++ b/packages/Tethering/res/values-da/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Netdeling eller hotspot er aktivt"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tryk for at konfigurere"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Netdeling er deaktiveret"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakt din administrator for at få oplysninger"</string>
+</resources>
diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml
new file mode 100644
index 0000000..9046cd5
--- /dev/null
+++ b/packages/Tethering/res/values-de/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oder Hotspot aktiv"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Zum Einrichten tippen."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering ist deaktiviert"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Bitte wende dich für weitere Informationen an den Administrator"</string>
+</resources>
diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml
new file mode 100644
index 0000000..3b9f537
--- /dev/null
+++ b/packages/Tethering/res/values-el/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Πατήστε για ρύθμιση."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Η σύνδεση είναι απενεργοποιημένη"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rAU/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rCA/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rGB/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rIN/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..7f47fc8
--- /dev/null
+++ b/packages/Tethering/res/values-en-rXC/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎Tap to set up.‎‏‎‎‏‎"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Tethering is disabled‎‏‎‎‏‎"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎Contact your admin for details‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..e4618b8
--- /dev/null
+++ b/packages/Tethering/res/values-es-rUS/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red o zona activa conectados"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Presiona para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Para obtener más información, comunícate con el administrador"</string>
+</resources>
diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml
new file mode 100644
index 0000000..8dc1575
--- /dev/null
+++ b/packages/Tethering/res/values-es/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"La conexión compartida está inhabilitada"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ponte en contacto con el administrador para obtener más información"</string>
+</resources>
diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml
new file mode 100644
index 0000000..872c8a7
--- /dev/null
+++ b/packages/Tethering/res/values-et/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Jagamine või kuumkoht on aktiivne"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Puudutage seadistamiseks."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Jagamine on keelatud"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
+</resources>
diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml
new file mode 100644
index 0000000..6c4605e
--- /dev/null
+++ b/packages/Tethering/res/values-eu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Konexioa partekatzea edo sare publikoa aktibo"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Sakatu konfiguratzeko."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Desgaituta dago konexioa partekatzeko aukera"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml
new file mode 100644
index 0000000..bc2ee23
--- /dev/null
+++ b/packages/Tethering/res/values-fa/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"برای راه‌اندازی ضربه بزنید."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml
new file mode 100644
index 0000000..ff0fca6
--- /dev/null
+++ b/packages/Tethering/res/values-fi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Internetin jakaminen tai yhteyspiste käytössä"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Määritä napauttamalla."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Yhteyden jakaminen poistettu käytöstä"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kysy lisätietoja järjestelmänvalvojalta."</string>
+</resources>
diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..1f5df0e
--- /dev/null
+++ b/packages/Tethering/res/values-fr-rCA/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Touchez pour configurer."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml
new file mode 100644
index 0000000..daf7c9d
--- /dev/null
+++ b/packages/Tethering/res/values-fr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Appuyez ici pour configurer."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Pour en savoir plus, contactez votre administrateur"</string>
+</resources>
diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml
new file mode 100644
index 0000000..0d16a1d
--- /dev/null
+++ b/packages/Tethering/res/values-gl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Conexión compartida ou zona wifi activada"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tocar para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"A conexión compartida está desactivada"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta co administrador para obter información"</string>
+</resources>
diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml
new file mode 100644
index 0000000..9d6b02f
--- /dev/null
+++ b/packages/Tethering/res/values-gu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"સેટ કરવા માટે ટૅપ કરો."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ટિથરિંગ અક્ષમ કરેલ છે"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml
new file mode 100644
index 0000000..9c29d9a
--- /dev/null
+++ b/packages/Tethering/res/values-hi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग या हॉटस्‍पॉट सक्रिय"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करने के लिए टैप करें."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml
new file mode 100644
index 0000000..d0d25bb
--- /dev/null
+++ b/packages/Tethering/res/values-hr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Ograničenje ili aktivan hotspot"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste postavili."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modemsko je povezivanje onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Obratite se administratoru da biste saznali pojedinosti"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml
new file mode 100644
index 0000000..3129659
--- /dev/null
+++ b/packages/Tethering/res/values-hu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Megosztás vagy aktív hotspot"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Koppintson a beállításhoz."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Az internetmegosztás le van tiltva"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"A részletekért forduljon rendszergazdájához"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml
new file mode 100644
index 0000000..8ba6435
--- /dev/null
+++ b/packages/Tethering/res/values-hy/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Մոդեմի ռեժիմը միացված է"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Հպեք՝ կարգավորելու համար:"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Մոդեմի ռեժիմն անջատված է"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
+</resources>
diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml
new file mode 100644
index 0000000..1e093ab
--- /dev/null
+++ b/packages/Tethering/res/values-in/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering (Penambatan) atau hotspot aktif"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Ketuk untuk menyiapkan."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering dinonaktifkan"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi admin untuk mengetahui detailnya"</string>
+</resources>
diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml
new file mode 100644
index 0000000..f5769d5
--- /dev/null
+++ b/packages/Tethering/res/values-is/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Kveikt á tjóðrun eða aðgangsstað"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Ýttu til að setja upp."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Slökkt er á tjóðrun"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml
new file mode 100644
index 0000000..e0b3724
--- /dev/null
+++ b/packages/Tethering/res/values-it/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oppure hotspot attivo"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tocca per impostare."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering disattivato"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
+</resources>
diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml
new file mode 100644
index 0000000..c002c44
--- /dev/null
+++ b/packages/Tethering/res/values-iw/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"שיתוף אינטרנט פעיל"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"הקש כדי להגדיר."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"שיתוף האינטרנט בין ניידים מושבת"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"לפרטים, יש לפנות למנהל המערכת"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml
new file mode 100644
index 0000000..314bde0
--- /dev/null
+++ b/packages/Tethering/res/values-ja/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"タップしてセットアップします。"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"テザリングは無効に設定されています"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳しくは、管理者にお問い合わせください"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml
new file mode 100644
index 0000000..7bbd81d
--- /dev/null
+++ b/packages/Tethering/res/values-ka/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"შეეხეთ დასაყენებლად."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ტეტერინგი გათიშულია"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
+</resources>
diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml
new file mode 100644
index 0000000..7fd87a1
--- /dev/null
+++ b/packages/Tethering/res/values-kk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Тетеринг немесе хотспот қосулы"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Реттеу үшін түртіңіз."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Тетеринг өшірілді"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Мәліметтерді әкімшіден алыңыз"</string>
+</resources>
diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml
new file mode 100644
index 0000000..2f85224
--- /dev/null
+++ b/packages/Tethering/res/values-km/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ប៉ះដើម្បីកំណត់"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ការភ្ជាប់​ត្រូវបានបិទ"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត"</string>
+</resources>
diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml
new file mode 100644
index 0000000..f11a83ea
--- /dev/null
+++ b/packages/Tethering/res/values-kn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml
new file mode 100644
index 0000000..57f24f5
--- /dev/null
+++ b/packages/Tethering/res/values-ko/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"테더링 또는 핫스팟 사용"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"설정하려면 탭하세요."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"테더링이 사용 중지됨"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"자세한 정보는 관리자에게 문의하세요."</string>
+</resources>
diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml
new file mode 100644
index 0000000..7985485
--- /dev/null
+++ b/packages/Tethering/res/values-ky/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Жалгаштыруу же хотспот жандырылган"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Жөндөө үчүн таптап коюңуз."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Жалгаштыруу функциясы өчүрүлгөн"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml
new file mode 100644
index 0000000..78f1585
--- /dev/null
+++ b/packages/Tethering/res/values-lo/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml
new file mode 100644
index 0000000..ebff8ac
--- /dev/null
+++ b/packages/Tethering/res/values-lt/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Susietas ar aktyvus"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Palieskite, kad nustatytumėte."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Įrenginio kaip modemo naudojimas išjungtas"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml
new file mode 100644
index 0000000..54d0048
--- /dev/null
+++ b/packages/Tethering/res/values-lv/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Piesaiste vai tīklājs ir aktīvs."</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Pieskarieties, lai iestatītu."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Piesaiste ir atspējota"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml
new file mode 100644
index 0000000..0fab8aa
--- /dev/null
+++ b/packages/Tethering/res/values-mk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Поврзувањето или точката на пристап се активни"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Допрете за поставување."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Врзувањето е оневозможено"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Контактирајте со администраторот за детали"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml
new file mode 100644
index 0000000..fd7e556
--- /dev/null
+++ b/packages/Tethering/res/values-ml/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml
new file mode 100644
index 0000000..4596577
--- /dev/null
+++ b/packages/Tethering/res/values-mn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Модем болгох эсвэл идэвхтэй цэг болгох"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Тохируулахын тулд товшино уу."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Модем болгох боломжгүй байна"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml
new file mode 100644
index 0000000..85c9ade
--- /dev/null
+++ b/packages/Tethering/res/values-mr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग बंद आहे"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml
new file mode 100644
index 0000000..ec6bdbd
--- /dev/null
+++ b/packages/Tethering/res/values-ms/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Penambatan atau titik panas aktif"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Ketik untuk membuat persediaan."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Penambatan dilumpuhkan"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi pentadbir anda untuk maklumat lanjut"</string>
+</resources>
diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml
new file mode 100644
index 0000000..83978b6
--- /dev/null
+++ b/packages/Tethering/res/values-my/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml
new file mode 100644
index 0000000..9abf32d
--- /dev/null
+++ b/packages/Tethering/res/values-nb/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Internettdeling eller trådløs sone er aktiv"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Trykk for å konfigurere."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internettdeling er slått av"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ta kontakt med administratoren din for å få mer informasjon"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml
new file mode 100644
index 0000000..c886929
--- /dev/null
+++ b/packages/Tethering/res/values-ne/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिङलाई असक्षम पारिएको छ"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
+</resources>
diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml
new file mode 100644
index 0000000..0ec4bff
--- /dev/null
+++ b/packages/Tethering/res/values-nl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering of hotspot actief"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om in te stellen."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is uitgeschakeld"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Neem contact op met je beheerder voor meer informatie"</string>
+</resources>
diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml
new file mode 100644
index 0000000..4576857
--- /dev/null
+++ b/packages/Tethering/res/values-or/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml
new file mode 100644
index 0000000..deddf2e
--- /dev/null
+++ b/packages/Tethering/res/values-pa/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml
new file mode 100644
index 0000000..48d8468
--- /dev/null
+++ b/packages/Tethering/res/values-pl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktywny tethering lub punkt dostępu"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Kliknij, by skonfigurować."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering został wyłączony"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..32c22b8
--- /dev/null
+++ b/packages/Tethering/res/values-pt-rBR/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..641e22f
--- /dev/null
+++ b/packages/Tethering/res/values-pt-rPT/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Ligação ponto a ponto ou hotspot activos"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"A ligação (à Internet) via telemóvel está desativada."</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacte o gestor para obter detalhes."</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml
new file mode 100644
index 0000000..32c22b8
--- /dev/null
+++ b/packages/Tethering/res/values-pt/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml
new file mode 100644
index 0000000..f861f73
--- /dev/null
+++ b/packages/Tethering/res/values-ro/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Atingeți ca să configurați."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tetheringul este dezactivat"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contactați administratorul pentru detalii"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml
new file mode 100644
index 0000000..027cb41
--- /dev/null
+++ b/packages/Tethering/res/values-ru/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Включен режим модема"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Нажмите, чтобы настроить."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Включить режим модема нельзя"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Обратитесь к администратору, чтобы узнать подробности."</string>
+</resources>
diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml
new file mode 100644
index 0000000..7d8599f
--- /dev/null
+++ b/packages/Tethering/res/values-si/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"පිහිටුවීමට තට්ටු කරන්න."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ටෙදරින් අබල කර ඇත"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml
new file mode 100644
index 0000000..a8fe297
--- /dev/null
+++ b/packages/Tethering/res/values-sk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering alebo prístupový bod je aktívny"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím prejdete na nastavenie."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je deaktivovaný"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požiadajte svojho správcu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml
new file mode 100644
index 0000000..b5e5e38
--- /dev/null
+++ b/packages/Tethering/res/values-sl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Dotaknite se, če želite nastaviti."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Za podrobnosti se obrnite na skrbnika"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml
new file mode 100644
index 0000000..fdd4906
--- /dev/null
+++ b/packages/Tethering/res/values-sq/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Trokit për ta konfiguruar."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Lidhja e çiftimit është çaktivizuar"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakto me administratorin për detaje"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml
new file mode 100644
index 0000000..9fab34589
--- /dev/null
+++ b/packages/Tethering/res/values-sr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Додирните да бисте подесили."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Привезивање је онемогућено"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Потражите детаље од администратора"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml
new file mode 100644
index 0000000..10eeb0f
--- /dev/null
+++ b/packages/Tethering/res/values-sv/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Internetdelning eller surfzon aktiverad"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Tryck om du vill konfigurera."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internetdelning har inaktiverats"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakta administratören om du vill veta mer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml
new file mode 100644
index 0000000..3353963
--- /dev/null
+++ b/packages/Tethering/res/values-sw/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml
new file mode 100644
index 0000000..b1e5cc2
--- /dev/null
+++ b/packages/Tethering/res/values-ta/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"அமைக்க, தட்டவும்."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
+</resources>
diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml
new file mode 100644
index 0000000..aae40de
--- /dev/null
+++ b/packages/Tethering/res/values-te/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"సెటప్ చేయడానికి నొక్కండి."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"టెథెరింగ్ నిలిపివేయబడింది"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string>
+</resources>
diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml
new file mode 100644
index 0000000..1b80056
--- /dev/null
+++ b/packages/Tethering/res/values-th/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"แตะเพื่อตั้งค่า"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
+</resources>
diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml
new file mode 100644
index 0000000..12863f9
--- /dev/null
+++ b/packages/Tethering/res/values-tl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Pagsasama o aktibong hotspot"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"I-tap upang i-set up."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Naka-disable ang pag-tether"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
+</resources>
diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml
new file mode 100644
index 0000000..bfcf1ac
--- /dev/null
+++ b/packages/Tethering/res/values-tr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering veya hotspot etkin"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Ayarlamak için dokunun."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering devre dışı bırakıldı"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
+</resources>
diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml
new file mode 100644
index 0000000..8e159c07
--- /dev/null
+++ b/packages/Tethering/res/values-uk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Прив\'язка чи точка дост. активна"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Торкніться, щоб налаштувати."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Використання телефона в режимі модема вимкнено"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml
new file mode 100644
index 0000000..89195d4
--- /dev/null
+++ b/packages/Tethering/res/values-ur/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ٹیدرنگ غیر فعال ہے"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
+</resources>
diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml
new file mode 100644
index 0000000..0ac4d4a
--- /dev/null
+++ b/packages/Tethering/res/values-uz/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Modem rejimi yoniq"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Sozlash uchun bosing."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modem rejimi faolsizlantirildi"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
+</resources>
diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml
new file mode 100644
index 0000000..85a4db8
--- /dev/null
+++ b/packages/Tethering/res/values-vi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Nhấn để thiết lập."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Đã tắt tính năng chia sẻ kết nối"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..ff1fe03
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rCN/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"网络共享或热点已启用"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"点按即可进行设置。"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"网络共享已停用"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"请与您的管理员联系以了解详情"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..0de39fa
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rHK/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"已啟用網絡共享或熱點"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"輕按即可設定。"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"網絡共享已停用"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"請聯絡您的管理員以瞭解詳情"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9a117bb
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rTW/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"網路共用或無線基地台已啟用"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"輕觸即可進行設定。"</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"數據連線已停用"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳情請洽你的管理員"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml
new file mode 100644
index 0000000..8fe10d8
--- /dev/null
+++ b/packages/Tethering/res/values-zu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Thepha ukuze usethe."</string>
+    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
+    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
+</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
new file mode 100644
index 0000000..ca866a9
--- /dev/null
+++ b/packages/Tethering/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Shown when the device is tethered -->
+    <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
+    <string name="tethered_notification_title">Tethering or hotspot active</string>
+    <!-- Strings for tethered notification message [CHAR LIMIT=200] -->
+    <string name="tethered_notification_message">Tap to set up.</string>
+
+    <!-- This notification is shown when tethering has been disabled on a user's device.
+    The device is managed by the user's employer. Tethering can't be turned on unless the
+    IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
+    <!-- Strings for tether disabling notification title  [CHAR LIMIT=200] -->
+    <string name="disable_tether_notification_title">Tethering is disabled</string>
+    <!-- Strings for tether disabling notification message  [CHAR LIMIT=200] -->
+    <string name="disable_tether_notification_message">Contact your admin for details</string>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index f1228129..7c78ef8 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -109,6 +109,7 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.tethering.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -675,14 +676,14 @@
         int icon = 0;
         switch(id) {
             case SystemMessage.NOTE_TETHER_USB:
-                icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+                icon = R.drawable.stat_sys_tether_usb;
                 break;
             case SystemMessage.NOTE_TETHER_BLUETOOTH:
-                icon = com.android.internal.R.drawable.stat_sys_tether_bluetooth;
+                icon = R.drawable.stat_sys_tether_bluetooth;
                 break;
             case SystemMessage.NOTE_TETHER_GENERAL:
             default:
-                icon = com.android.internal.R.drawable.stat_sys_tether_general;
+                icon = R.drawable.stat_sys_tether_general;
                 break;
         }
 
@@ -702,16 +703,16 @@
         PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0,
                 null, UserHandle.CURRENT);
 
-        Resources r = Resources.getSystem();
+        Resources r = mContext.getResources();
         final CharSequence title;
         final CharSequence message;
 
         if (tetheringOn) {
-            title = r.getText(com.android.internal.R.string.tethered_notification_title);
-            message = r.getText(com.android.internal.R.string.tethered_notification_message);
+            title = r.getText(R.string.tethered_notification_title);
+            message = r.getText(R.string.tethered_notification_message);
         } else {
-            title = r.getText(com.android.internal.R.string.disable_tether_notification_title);
-            message = r.getText(com.android.internal.R.string.disable_tether_notification_message);
+            title = r.getText(R.string.disable_tether_notification_title);
+            message = r.getText(R.string.disable_tether_notification_message);
         }
 
         if (mTetheredNotificationBuilder == null) {
@@ -909,7 +910,7 @@
 
             if (newlyDisallowed && isTetheringActiveOnDevice) {
                 mWrapper.showTetheredNotification(
-                        com.android.internal.R.drawable.stat_sys_tether_general, false);
+                        R.drawable.stat_sys_tether_general, false);
                 mWrapper.untetherAll();
             }
         }
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 469bdc6..e4de625 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -21,7 +21,6 @@
 
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
 
     <application android:label="VpnDialogs"
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 2eb2f33..203bc61 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -162,3 +162,9 @@
     name: "protolog.conf.json.gz",
     src: ":services.core.json.gz",
 }
+
+platform_compat_config {
+    name: "services-core-platform-compat-config",
+    src: ":services.core.unboosted",
+}
+
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 798a4c6..a318844 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -74,6 +74,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
@@ -695,6 +696,35 @@
         return mIsHearingAidProfileSupported;
     }
 
+    @Override
+    /** @hide */
+    public java.util.List<String> getSystemConfigEnabledProfilesForPackage(String packageName) {
+        if (Binder.getCallingUid() != Process.BLUETOOTH_UID) {
+            Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth");
+            return null;
+        }
+
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        if (systemConfig == null) {
+            return null;
+        }
+
+        android.util.ArrayMap<String, Boolean> componentEnabledStates =
+                systemConfig.getComponentsEnabledStates(packageName);
+        if (componentEnabledStates == null) {
+            return null;
+        }
+
+        ArrayList enabledProfiles = new ArrayList<String>();
+        for (Map.Entry<String, Boolean> entry : componentEnabledStates.entrySet()) {
+            if (entry.getValue()) {
+                enabledProfiles.add(entry.getKey());
+            }
+        }
+
+        return enabledProfiles;
+    }
+
     // Monitor change of BLE scan only mode settings.
     private void registerForBleScanModeChange() {
         ContentObserver contentObserver = new ContentObserver(null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a3a6172..0ad275f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1395,7 +1395,7 @@
 
     @Override
     public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
-        enforceConnectivityInternalPermission();
+        NetworkStack.checkNetworkStackPermission(mContext);
         return getActiveNetworkForUidInternal(uid, ignoreBlocked);
     }
 
@@ -1437,7 +1437,7 @@
 
     @Override
     public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
-        enforceConnectivityInternalPermission();
+        NetworkStack.checkNetworkStackPermission(mContext);
         final NetworkState state = getUnfilteredActiveNetworkState(uid);
         filterNetworkStateForUid(state, uid, ignoreBlocked);
         return state.networkInfo;
@@ -1656,8 +1656,8 @@
 
     @Override
     public NetworkState[] getAllNetworkState() {
-        // Require internal since we're handing out IMSI details
-        enforceConnectivityInternalPermission();
+        // This contains IMSI details, so make sure the caller is privileged.
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         final ArrayList<NetworkState> result = Lists.newArrayList();
         for (Network network : getAllNetworks()) {
@@ -1735,7 +1735,7 @@
         }
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
-            enforceConnectivityInternalPermission();
+            enforceConnectivityRestrictedNetworksPermission();
         }
 
         InetAddress addr;
@@ -2005,6 +2005,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
+    private void enforceNetworkFactoryPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_FACTORY,
+                "ConnectivityService");
+    }
+
     private boolean checkSettingsPermission() {
         return checkAnyPermissionOf(
                 android.Manifest.permission.NETWORK_SETTINGS,
@@ -2024,18 +2030,19 @@
                 "ConnectivityService");
     }
 
-    private void enforceConnectivityInternalPermission() {
-        enforceAnyPermissionOf(
-                android.Manifest.permission.CONNECTIVITY_INTERNAL,
-                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
-    }
-
     private void enforceControlAlwaysOnVpnPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
                 "ConnectivityService");
     }
 
+    private void enforceNetworkStackOrSettingsPermission() {
+        enforceAnyPermissionOf(
+                android.Manifest.permission.NETWORK_SETTINGS,
+                android.Manifest.permission.NETWORK_STACK,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+    }
+
     private void enforceNetworkStackSettingsOrSetup() {
         enforceAnyPermissionOf(
                 android.Manifest.permission.NETWORK_SETTINGS,
@@ -2063,7 +2070,11 @@
                     "ConnectivityService");
             return;
         } catch (SecurityException e) { /* fallback to ConnectivityInternalPermission */ }
-        enforceConnectivityInternalPermission();
+        //  TODO: Remove this fallback check after all apps have declared
+        //   CONNECTIVITY_USE_RESTRICTED_NETWORKS.
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                "ConnectivityService");
     }
 
     private void enforceKeepalivePermission() {
@@ -2072,7 +2083,7 @@
 
     // Public because it's used by mLockdownTracker.
     public void sendConnectedBroadcast(NetworkInfo info) {
-        enforceConnectivityInternalPermission();
+        NetworkStack.checkNetworkStackPermission(mContext);
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
@@ -3589,7 +3600,7 @@
 
     @Override
     public void startCaptivePortalApp(Network network) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackOrSettingsPermission();
         mHandler.post(() -> {
             NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
             if (nai == null) return;
@@ -4080,7 +4091,7 @@
 
     @Override
     public String[] getTetheredDhcpRanges() {
-        enforceConnectivityInternalPermission();
+        enforceSettingsPermission();
         return mTetheringManager.getTetheredDhcpRanges();
     }
 
@@ -4304,7 +4315,7 @@
 
     @Override
     public void setGlobalProxy(final ProxyInfo proxyProperties) {
-        enforceConnectivityInternalPermission();
+        NetworkStack.checkNetworkStackPermission(mContext);
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
@@ -4843,7 +4854,7 @@
 
     @Override
     public String getMobileProvisioningUrl() {
-        enforceConnectivityInternalPermission();
+        enforceSettingsPermission();
         String url = getProvisioningUrlBaseFromFile();
         if (TextUtils.isEmpty(url)) {
             url = mContext.getResources().getString(R.string.mobile_provisioning_url);
@@ -4869,7 +4880,7 @@
     @Override
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
-        enforceConnectivityInternalPermission();
+        enforceSettingsPermission();
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             return;
         }
@@ -5457,7 +5468,7 @@
 
     @Override
     public int registerNetworkFactory(Messenger messenger, String name) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkFactoryPermission();
         NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(),
                 NetworkFactory.SerialNumber.nextSerialNumber());
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
@@ -5472,7 +5483,7 @@
 
     @Override
     public void unregisterNetworkFactory(Messenger messenger) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkFactoryPermission();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
     }
 
@@ -5571,7 +5582,7 @@
     public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkMisc networkMisc, int factorySerialNumber) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkFactoryPermission();
 
         LinkProperties lp = new LinkProperties(linkProperties);
         lp.ensureDirectlyConnectedRoutes();
@@ -6955,7 +6966,7 @@
 
     @Override
     public String getCaptivePortalServerUrl() {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackOrSettingsPermission();
         String settingUrl = mContext.getResources().getString(
                 R.string.config_networkCaptivePortalServerUrl);
 
@@ -7008,7 +7019,7 @@
 
     @Override
     public void factoryReset() {
-        enforceConnectivityInternalPermission();
+        enforceSettingsPermission();
 
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9ebe896..f3089c1 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -146,8 +146,13 @@
 
         @Override
         public void onBootPhase(int phase) {
-            if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
-                mService.systemRunning();
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                // the location service must be functioning after this boot phase
+                mService.onSystemReady();
+            } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+                // some providers rely on third party code, so we wait to initialize
+                // providers until third party code is allowed to run
+                mService.onSystemThirdPartyAppsCanStart();
             }
         }
     }
@@ -264,156 +269,155 @@
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationExtraPackageNames));
 
-        // most startup is deferred until systemRunning()
+        // most startup is deferred until systemReady()
     }
 
-    private void systemRunning() {
+    private void onSystemReady() {
         synchronized (mLock) {
-            initializeLocked();
-        }
-    }
+            mPackageManager = mContext.getPackageManager();
+            mAppOps = mContext.getSystemService(AppOpsManager.class);
+            mPowerManager = mContext.getSystemService(PowerManager.class);
+            mActivityManager = mContext.getSystemService(ActivityManager.class);
+            mUserManager = mContext.getSystemService(UserManager.class);
 
-    @GuardedBy("mLock")
-    private void initializeLocked() {
-        mPackageManager = mContext.getPackageManager();
-        mAppOps = mContext.getSystemService(AppOpsManager.class);
-        mPowerManager = mContext.getSystemService(PowerManager.class);
-        mActivityManager = mContext.getSystemService(ActivityManager.class);
-        mUserManager = mContext.getSystemService(UserManager.class);
+            mSettingsStore = new LocationSettingsStore(mContext, mHandler);
+            mLocationFudger = new LocationFudger(mContext, mHandler);
+            mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
 
-        mSettingsStore = new LocationSettingsStore(mContext, mHandler);
+            PowerManagerInternal localPowerManager =
+                    LocalServices.getService(PowerManagerInternal.class);
 
-        mLocationFudger = new LocationFudger(mContext, mHandler);
-        mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
-
-        PowerManagerInternal localPowerManager =
-                LocalServices.getService(PowerManagerInternal.class);
-
-        // prepare providers
-        initializeProvidersLocked();
-
-        // add listeners
-        mAppOps.startWatchingMode(
-                AppOpsManager.OP_COARSE_LOCATION,
-                null,
-                AppOpsManager.WATCH_FOREGROUND_CHANGES,
-                new AppOpsManager.OnOpChangedInternalListener() {
-                    public void onOpChanged(int op, String packageName) {
-                        // onOpChanged invoked on ui thread, move to our thread to reduce risk of
+            // add listeners
+            mAppOps.startWatchingMode(
+                    AppOpsManager.OP_COARSE_LOCATION,
+                    null,
+                    AppOpsManager.WATCH_FOREGROUND_CHANGES,
+                    new AppOpsManager.OnOpChangedInternalListener() {
+                        public void onOpChanged(int op, String packageName) {
+                            // onOpChanged invoked on ui thread, move to our thread to reduce risk
+                            // of blocking ui thread
+                            mHandler.post(() -> {
+                                synchronized (mLock) {
+                                    onAppOpChangedLocked();
+                                }
+                            });
+                        }
+                    });
+            mPackageManager.addOnPermissionsChangeListener(
+                    uid -> {
+                        // listener invoked on ui thread, move to our thread to reduce risk of
                         // blocking ui thread
                         mHandler.post(() -> {
                             synchronized (mLock) {
-                                onAppOpChangedLocked();
+                                onPermissionsChangedLocked();
                             }
                         });
-                    }
-                });
-        mPackageManager.addOnPermissionsChangeListener(
-                uid -> {
-                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
-                    // ui thread
-                    mHandler.post(() -> {
-                        synchronized (mLock) {
-                            onPermissionsChangedLocked();
-                        }
                     });
-                });
-        mActivityManager.addOnUidImportanceListener(
-                (uid, importance) -> {
-                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
-                    // ui thread
-                    mHandler.post(() -> {
-                        synchronized (mLock) {
-                            onUidImportanceChangedLocked(uid, importance);
-                        }
+            mActivityManager.addOnUidImportanceListener(
+                    (uid, importance) -> {
+                        // listener invoked on ui thread, move to our thread to reduce risk of
+                        // blocking ui thread
+                        mHandler.post(() -> {
+                            synchronized (mLock) {
+                                onUidImportanceChangedLocked(uid, importance);
+                            }
+                        });
+                    },
+                    FOREGROUND_IMPORTANCE_CUTOFF);
+
+            localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
+                    state -> {
+                        // listener invoked on ui thread, move to our thread to reduce risk of
+                        // blocking ui thread
+                        mHandler.post(() -> {
+                            synchronized (mLock) {
+                                onBatterySaverModeChangedLocked(state.locationMode);
+                            }
+                        });
                     });
-                },
-                FOREGROUND_IMPORTANCE_CUTOFF);
+            mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
 
-        localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
-                state -> {
-                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
-                    // ui thread
-                    mHandler.post(() -> {
-                        synchronized (mLock) {
-                            onBatterySaverModeChangedLocked(state.locationMode);
-                        }
-                    });
-                });
-        mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
-
-        mSettingsStore.addOnLocationEnabledChangedListener(() -> {
-            synchronized (mLock) {
-                onLocationModeChangedLocked(true);
-            }
-        });
-        mSettingsStore.addOnLocationProvidersAllowedChangedListener(() -> {
-            synchronized (mLock) {
-                onProviderAllowedChangedLocked();
-            }
-        });
-        mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
-            synchronized (mLock) {
-                onBackgroundThrottleIntervalChangedLocked();
-            }
-        });
-        mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
-            synchronized (mLock) {
-                onBackgroundThrottleWhitelistChangedLocked();
-            }
-        });
-        mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
-            synchronized (mLock) {
-                onIgnoreSettingsWhitelistChangedLocked();
-            }
-        });
-
-        new PackageMonitor() {
-            @Override
-            public void onPackageDisappeared(String packageName, int reason) {
+            mSettingsStore.addOnLocationEnabledChangedListener((userId) -> {
                 synchronized (mLock) {
-                    LocationManagerService.this.onPackageDisappearedLocked(packageName);
+                    onLocationModeChangedLocked(userId, true);
                 }
-            }
-        }.register(mContext, mHandler.getLooper(), true);
-
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
-        intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (action == null) {
-                    return;
-                }
+            });
+            mSettingsStore.addOnLocationProvidersAllowedChangedListener((userId) -> {
                 synchronized (mLock) {
-                    switch (action) {
-                        case Intent.ACTION_USER_SWITCHED:
-                            onUserChangedLocked(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                            break;
-                        case Intent.ACTION_MANAGED_PROFILE_ADDED:
-                        case Intent.ACTION_MANAGED_PROFILE_REMOVED:
-                            onUserProfilesChangedLocked();
-                            break;
-                        case Intent.ACTION_SCREEN_ON:
-                        case Intent.ACTION_SCREEN_OFF:
-                            onScreenStateChangedLocked();
-                            break;
+                    onProviderAllowedChangedLocked(userId);
+                }
+            });
+            mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
+                synchronized (mLock) {
+                    onBackgroundThrottleIntervalChangedLocked();
+                }
+            });
+            mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
+                synchronized (mLock) {
+                    onBackgroundThrottleWhitelistChangedLocked();
+                }
+            });
+            mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
+                synchronized (mLock) {
+                    onIgnoreSettingsWhitelistChangedLocked();
+                }
+            });
+
+            new PackageMonitor() {
+                @Override
+                public void onPackageDisappeared(String packageName, int reason) {
+                    synchronized (mLock) {
+                        LocationManagerService.this.onPackageDisappearedLocked(packageName);
                     }
                 }
-            }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
+            }.register(mContext, mHandler.getLooper(), true);
 
-        // switching the user from null to system here performs the bulk of the initialization work.
-        // the user being changed will cause a reload of all user specific settings, which causes
-        // provider initialization, and propagates changes until a steady state is reached
-        mCurrentUserId = UserHandle.USER_NULL;
-        onUserChangedLocked(ActivityManager.getCurrentUser());
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+            intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+            intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+            intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+
+            mContext.registerReceiverAsUser(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    final String action = intent.getAction();
+                    if (action == null) {
+                        return;
+                    }
+                    synchronized (mLock) {
+                        switch (action) {
+                            case Intent.ACTION_USER_SWITCHED:
+                                onUserChangedLocked(
+                                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                                break;
+                            case Intent.ACTION_MANAGED_PROFILE_ADDED:
+                            case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+                                onUserProfilesChangedLocked();
+                                break;
+                            case Intent.ACTION_SCREEN_ON:
+                            case Intent.ACTION_SCREEN_OFF:
+                                onScreenStateChangedLocked();
+                                break;
+                        }
+                    }
+                }
+            }, UserHandle.ALL, intentFilter, null, mHandler);
+
+            // switching the user from null to system here performs the bulk of the initialization
+            // work. the user being changed will cause a reload of all user specific settings, which
+            // causes initialization, and propagates changes until a steady state is reached
+            mCurrentUserId = UserHandle.USER_NULL;
+            onUserChangedLocked(ActivityManager.getCurrentUser());
+        }
+    }
+
+    private void onSystemThirdPartyAppsCanStart() {
+        synchronized (mLock) {
+            // prepare providers
+            initializeProvidersLocked();
+        }
     }
 
     @GuardedBy("mLock")
@@ -463,7 +467,11 @@
     }
 
     @GuardedBy("mLock")
-    private void onLocationModeChangedLocked(boolean broadcast) {
+    private void onLocationModeChangedLocked(int userId, boolean broadcast) {
+        if (!isCurrentProfileLocked(userId)) {
+            return;
+        }
+
         if (D) {
             Log.d(TAG, "location enabled is now " + isLocationEnabled());
         }
@@ -482,7 +490,11 @@
     }
 
     @GuardedBy("mLock")
-    private void onProviderAllowedChangedLocked() {
+    private void onProviderAllowedChangedLocked(int userId) {
+        if (!isCurrentProfileLocked(userId)) {
+            return;
+        }
+
         for (LocationProvider p : mProviders) {
             p.onAllowedChangedLocked();
         }
@@ -795,8 +807,8 @@
         onUserProfilesChangedLocked();
 
         // if the user changes, per-user settings may also have changed
-        onLocationModeChangedLocked(false);
-        onProviderAllowedChangedLocked();
+        onLocationModeChangedLocked(userId, false);
+        onProviderAllowedChangedLocked(userId);
 
         // always force useability to be rechecked, even if no per-user settings have changed
         for (LocationProvider p : mProviders) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9efaad8..e79a289 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -16,9 +16,8 @@
 
 package com.android.server;
 
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.NETWORK_STACK;
+import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.net.INetd.FIREWALL_BLACKLIST;
 import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
@@ -54,6 +53,7 @@
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkStack;
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
@@ -312,13 +312,13 @@
 
     @Override
     public void registerObserver(INetworkManagementEventObserver observer) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         mObservers.register(observer);
     }
 
     @Override
     public void unregisterObserver(INetworkManagementEventObserver observer) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         mObservers.unregister(observer);
     }
 
@@ -453,7 +453,7 @@
 
     @Override
     public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) {
-        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         Preconditions.checkNotNull(provider);
         synchronized(mTetheringStatsProviders) {
             mTetheringStatsProviders.put(provider, name);
@@ -462,7 +462,7 @@
 
     @Override
     public void unregisterTetheringStatsProvider(ITetheringStatsProvider provider) {
-        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         synchronized(mTetheringStatsProviders) {
             mTetheringStatsProviders.remove(provider);
         }
@@ -470,7 +470,7 @@
 
     @Override
     public void tetherLimitReached(ITetheringStatsProvider provider) {
-        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         synchronized(mTetheringStatsProviders) {
             if (!mTetheringStatsProviders.containsKey(provider)) {
                 return;
@@ -737,7 +737,7 @@
     //
     @Override
     public String[] listInterfaces() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             return mNetdService.interfaceGetList();
         } catch (RemoteException | ServiceSpecificException e) {
@@ -787,7 +787,7 @@
 
     @Override
     public InterfaceConfiguration getInterfaceConfig(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         final InterfaceConfigurationParcel result;
         try {
             result = mNetdService.interfaceGetCfg(iface);
@@ -805,7 +805,7 @@
 
     @Override
     public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         LinkAddress linkAddr = cfg.getLinkAddress();
         if (linkAddr == null || linkAddr.getAddress() == null) {
             throw new IllegalStateException("Null LinkAddress given");
@@ -822,7 +822,7 @@
 
     @Override
     public void setInterfaceDown(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
         ifcg.setInterfaceDown();
         setInterfaceConfig(iface, ifcg);
@@ -830,7 +830,7 @@
 
     @Override
     public void setInterfaceUp(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
         ifcg.setInterfaceUp();
         setInterfaceConfig(iface, ifcg);
@@ -838,7 +838,7 @@
 
     @Override
     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.interfaceSetIPv6PrivacyExtensions(iface, enable);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -850,7 +850,7 @@
        IPv6 addresses on interface down, but we need to do full clean up here */
     @Override
     public void clearInterfaceAddresses(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.interfaceClearAddrs(iface);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -860,7 +860,7 @@
 
     @Override
     public void enableIpv6(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.interfaceSetEnableIPv6(iface, true);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -879,7 +879,7 @@
 
     @Override
     public void disableIpv6(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.interfaceSetEnableIPv6(iface, false);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -898,7 +898,7 @@
     }
 
     private void modifyRoute(boolean add, int netId, RouteInfo route) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         final String ifName = route.getInterface();
         final String dst = route.getDestination().toString();
@@ -963,7 +963,7 @@
 
     @Override
     public void setMtu(String iface, int mtu) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.interfaceSetMtu(iface, mtu);
@@ -982,7 +982,7 @@
 
     @Override
     public boolean getIpForwardingEnabled() throws IllegalStateException{
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             final boolean isEnabled = mNetdService.ipfwdEnabled();
@@ -994,7 +994,7 @@
 
     @Override
     public void setIpForwardingEnabled(boolean enable) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             if (enable) {
                 mNetdService.ipfwdEnableForwarding("tethering");
@@ -1013,7 +1013,7 @@
 
     @Override
     public void startTetheringWithConfiguration(boolean usingLegacyDnsProxy, String[] dhcpRange) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         // an odd number of addrs will fail
         try {
             mNetdService.tetherStartWithConfiguration(usingLegacyDnsProxy, dhcpRange);
@@ -1024,7 +1024,7 @@
 
     @Override
     public void stopTethering() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.tetherStop();
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1034,7 +1034,7 @@
 
     @Override
     public boolean isTetheringStarted() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             final boolean isEnabled = mNetdService.tetherIsEnabled();
@@ -1046,7 +1046,7 @@
 
     @Override
     public void tetherInterface(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.tetherInterfaceAdd(iface);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1061,7 +1061,7 @@
 
     @Override
     public void untetherInterface(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.tetherInterfaceRemove(iface);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1073,7 +1073,7 @@
 
     @Override
     public String[] listTetheredInterfaces() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             return mNetdService.tetherInterfaceList();
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1083,7 +1083,7 @@
 
     @Override
     public void setDnsForwarders(Network network, String[] dns) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
 
@@ -1096,7 +1096,7 @@
 
     @Override
     public String[] getDnsForwarders() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             return mNetdService.tetherDnsList();
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1127,19 +1127,19 @@
 
     @Override
     public void startInterfaceForwarding(String fromIface, String toIface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         modifyInterfaceForward(true, fromIface, toIface);
     }
 
     @Override
     public void stopInterfaceForwarding(String fromIface, String toIface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         modifyInterfaceForward(false, fromIface, toIface);
     }
 
     @Override
     public void enableNat(String internalInterface, String externalInterface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.tetherAddForward(internalInterface, externalInterface);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1149,7 +1149,7 @@
 
     @Override
     public void disableNat(String internalInterface, String externalInterface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.tetherRemoveForward(internalInterface, externalInterface);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -1159,7 +1159,7 @@
 
     @Override
     public void addIdleTimer(String iface, int timeout, final int type) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         if (DBG) Slog.d(TAG, "Adding idletimer");
 
@@ -1189,7 +1189,7 @@
 
     @Override
     public void removeIdleTimer(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         if (DBG) Slog.d(TAG, "Removing idletimer");
 
@@ -1213,7 +1213,7 @@
 
     @Override
     public void setInterfaceQuota(String iface, long quotaBytes) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         synchronized (mQuotaLock) {
             if (mActiveQuotas.containsKey(iface)) {
@@ -1244,7 +1244,7 @@
 
     @Override
     public void removeInterfaceQuota(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         synchronized (mQuotaLock) {
             if (!mActiveQuotas.containsKey(iface)) {
@@ -1277,7 +1277,7 @@
 
     @Override
     public void setInterfaceAlert(String iface, long alertBytes) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         // quick sanity check
         if (!mActiveQuotas.containsKey(iface)) {
@@ -1301,7 +1301,7 @@
 
     @Override
     public void removeInterfaceAlert(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         synchronized (mQuotaLock) {
             if (!mActiveAlerts.containsKey(iface)) {
@@ -1321,7 +1321,7 @@
 
     @Override
     public void setGlobalAlert(long alertBytes) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.bandwidthSetGlobalAlert(alertBytes);
@@ -1331,7 +1331,7 @@
     }
 
     private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         synchronized (mQuotaLock) {
             boolean oldEnable;
@@ -1431,7 +1431,7 @@
     @Override
     public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
             throws ServiceSpecificException {
-        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges));
         } catch (ServiceSpecificException e) {
@@ -1472,7 +1472,7 @@
     @Override
     public void setUidCleartextNetworkPolicy(int uid, int policy) {
         if (Binder.getCallingUid() != uid) {
-            mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+            NetworkStack.checkNetworkStackPermission(mContext);
         }
 
         synchronized (mQuotaLock) {
@@ -1506,7 +1506,6 @@
 
     @Override
     public boolean isBandwidthControlEnabled() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         return true;
     }
 
@@ -1557,7 +1556,7 @@
 
     @Override
     public NetworkStats getNetworkStatsTethering(int how) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
         synchronized (mTetheringStatsProviders) {
@@ -1575,7 +1574,7 @@
 
     @Override
     public void addVpnUidRanges(int netId, UidRange[] ranges) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkAddUidRanges(netId, toStableParcels(ranges));
@@ -1586,7 +1585,7 @@
 
     @Override
     public void removeVpnUidRanges(int netId, UidRange[] ranges) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges));
         } catch (RemoteException | ServiceSpecificException e) {
@@ -2020,7 +2019,7 @@
     }
 
     private void modifyInterfaceInNetwork(boolean add, int netId, String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             if (add) {
                 mNetdService.networkAddInterface(netId, iface);
@@ -2034,7 +2033,7 @@
 
     @Override
     public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         final LinkAddress la = routeInfo.getDestinationLinkAddress();
         final String ifName = routeInfo.getInterface();
@@ -2055,7 +2054,7 @@
 
     @Override
     public void setDefaultNetId(int netId) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkSetDefault(netId);
@@ -2066,7 +2065,7 @@
 
     @Override
     public void clearDefaultNetId() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkClearDefault();
@@ -2077,7 +2076,7 @@
 
     @Override
     public void setNetworkPermission(int netId, int permission) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkSetPermissionForNetwork(netId, permission);
@@ -2088,7 +2087,7 @@
 
     @Override
     public void allowProtect(int uid) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkSetProtectAllow(uid);
@@ -2099,7 +2098,7 @@
 
     @Override
     public void denyProtect(int uid) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        NetworkStack.checkNetworkStackPermission(mContext);
 
         try {
             mNetdService.networkSetProtectDeny(uid);
@@ -2145,7 +2144,7 @@
 
     @Override
     public boolean isNetworkRestricted(int uid) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         return isNetworkRestrictedInternal(uid);
     }
 
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 3efef01..b9b7bf7 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -16,19 +16,18 @@
 
 package com.android.server;
 
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.net.NetworkStack;
 import android.net.Uri;
-import android.net.nsd.NsdServiceInfo;
 import android.net.nsd.DnsSdTxtRecord;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
-import android.os.Binder;
-import android.os.HandlerThread;
+import android.net.nsd.NsdServiceInfo;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.UserHandle;
@@ -38,6 +37,12 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.net.InetAddress;
@@ -45,13 +50,6 @@
 import java.util.HashMap;
 import java.util.concurrent.CountDownLatch;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
 /**
  * Network Service Discovery Service handles remote service discovery operation requests by
  * implementing the INsdManager interface.
@@ -565,8 +563,7 @@
     }
 
     public void setEnabled(boolean isEnabled) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
-                "NsdService");
+        NetworkStack.checkNetworkStackPermission(mContext);
         mNsdSettings.putEnabledStatus(isEnabled);
         notifyEnabled(isEnabled);
     }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a4bc3bd..d4889ea 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -118,6 +118,7 @@
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.DataUnit;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -354,8 +355,7 @@
     @GuardedBy("mLock")
     private String mMoveTargetUuid;
 
-    @Nullable
-    private volatile String mMediaStoreAuthorityPackageName = null;
+    private volatile int mMediaStoreAuthorityAppId = -1;
 
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
@@ -819,7 +819,6 @@
                     refreshFuseSettings();
                 });
         refreshIsolatedStorageSettings();
-        refreshFuseSettings();
     }
 
     /**
@@ -883,16 +882,25 @@
         SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res));
     }
 
+    /**
+     * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is
+     * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse)
+     * whenever the user reboots.
+     */
     private void refreshFuseSettings() {
         int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                 FUSE_ENABLED, 0);
         if (isFuseEnabled == 1) {
-            SystemProperties.set(StorageManager.PROP_FUSE, "true");
+            Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on");
+            SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
+                    + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true");
         } else if (isFuseEnabled == -1) {
-            SystemProperties.set(StorageManager.PROP_FUSE, "false");
+            Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off");
+            SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
+                    + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false");
         }
         // else, keep the build config.
-        // This can be overridden be direct adjustment of persist.sys.prop
+        // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse
     }
 
     /**
@@ -1548,6 +1556,8 @@
     public StorageManagerService(Context context) {
         sSelf = this;
 
+        updateFusePropFromSettings();
+
         // Snapshot feature flag used for this boot
         SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
                 SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
@@ -1609,6 +1619,23 @@
         }
     }
 
+    /**
+     *  Checks if user changed the persistent settings_fuse flag from Settings UI
+     *  and updates PROP_FUSE (reboots if changed).
+     */
+    private void updateFusePropFromSettings() {
+        Boolean settingsFuseFlag = SystemProperties.getBoolean((FeatureFlagUtils.PERSIST_PREFIX
+                + FeatureFlagUtils.SETTINGS_FUSE_FLAG), false);
+        Slog.d(TAG, "The value of Settings Fuse Flag is " + settingsFuseFlag);
+        if (SystemProperties.getBoolean(StorageManager.PROP_FUSE, false) != settingsFuseFlag) {
+            Slog.d(TAG, "Set persist.sys.fuse to " + settingsFuseFlag);
+            SystemProperties.set(StorageManager.PROP_FUSE, Boolean.toString(settingsFuseFlag));
+            // Perform hard reboot to kick policy into place
+            mContext.getSystemService(PowerManager.class).reboot("Reboot device for FUSE system"
+                    + "property change to take effect");
+        }
+    }
+
     private void start() {
         connectStoraged();
         connectVold();
@@ -1697,7 +1724,7 @@
                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.getUserId(UserHandle.USER_SYSTEM));
         if (provider != null) {
-            mMediaStoreAuthorityPackageName = provider.packageName;
+            mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
         }
 
         try {
@@ -3724,8 +3751,10 @@
                 return Zygote.MOUNT_EXTERNAL_NONE;
             }
 
-            if (mIsFuseEnabled && packageName.equals(mMediaStoreAuthorityPackageName)) {
-                // Determine if caller requires pass_through mount
+            if (mIsFuseEnabled && mMediaStoreAuthorityAppId == UserHandle.getAppId(uid)) {
+                // Determine if caller requires pass_through mount; note that we do this for
+                // all processes that share a UID with MediaProvider; but this is fine, since
+                // those processes anyway share the same rights as MediaProvider.
                 return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
             }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index dc61261..24cd21e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -38,6 +38,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.Annotation;
 import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
@@ -1576,8 +1577,8 @@
                         && (mDataConnectionState[phoneId] != state
                         || mDataConnectionNetworkType[phoneId] != networkType)) {
                     String str = "onDataConnectionStateChanged("
-                            + TelephonyManager.dataStateToString(state)
-                            + ", " + TelephonyManager.getNetworkTypeName(networkType)
+                            + dataStateToString(state)
+                            + ", " + getNetworkTypeName(networkType)
                             + ") subId=" + subId + ", phoneId=" + phoneId;
                     log(str);
                     mLocalLog.log(str);
@@ -2689,4 +2690,74 @@
             }
         }
     }
+
+    /**
+     * Convert data state to string
+     *
+     * @return The data state in string format.
+     */
+    private String dataStateToString(@TelephonyManager.DataState int state) {
+        switch (state) {
+            case TelephonyManager.DATA_DISCONNECTED: return "DISCONNECTED";
+            case TelephonyManager.DATA_CONNECTING: return "CONNECTING";
+            case TelephonyManager.DATA_CONNECTED: return "CONNECTED";
+            case TelephonyManager.DATA_SUSPENDED: return "SUSPENDED";
+        }
+        return "UNKNOWN(" + state + ")";
+    }
+
+    /**
+     * Returns a string representation of the radio technology (network type)
+     * currently in use on the device.
+     * @param subId for which network type is returned
+     * @return the name of the radio technology
+     *
+     */
+    private String getNetworkTypeName(@Annotation.NetworkType int type) {
+        switch (type) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+                return "GPRS";
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+                return "EDGE";
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return "UMTS";
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+                return "HSDPA";
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+                return "HSUPA";
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                return "HSPA";
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return "CDMA";
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return "CDMA - EvDo rev. 0";
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                return "CDMA - EvDo rev. A";
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                return "CDMA - EvDo rev. B";
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return "CDMA - 1xRTT";
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return "LTE";
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+                return "CDMA - eHRPD";
+            case TelephonyManager.NETWORK_TYPE_IDEN:
+                return "iDEN";
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return "HSPA+";
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return "GSM";
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return "TD_SCDMA";
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
+                return "IWLAN";
+            case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                return "LTE_CA";
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return "NR";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e119d1e..53a5fc6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -568,11 +568,6 @@
     private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
     private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
 
-    /**
-     * How long between a process kill and we actually receive its death recipient
-     */
-    private static final long PROC_KILL_TIMEOUT = 5000; // 5 seconds;
-
     OomAdjuster mOomAdjuster;
     final LowMemDetector mLowMemDetector;
 
@@ -3743,9 +3738,6 @@
                 "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
             handleAppDiedLocked(app, false, true);
 
-            // Execute the callback if there is any.
-            doAppDiedCallbackLocked(app);
-
             if (doOomAdj) {
                 updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
             }
@@ -3758,9 +3750,6 @@
                     "Process " + app.processName + " (pid " + pid
                             + ") has died and restarted (pid " + app.pid + ").", app.info.uid);
 
-            // Execute the callback if there is any.
-            doAppDiedCallbackLocked(app);
-
             EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
                     app.setProcState);
         } else if (DEBUG_PROCESSES) {
@@ -3775,39 +3764,6 @@
         }
     }
 
-    @GuardedBy("this")
-    private void doAppDiedCallbackLocked(final ProcessRecord app) {
-        if (app.mAppDiedCallback != null) {
-            app.mAppDiedCallback.run();
-            app.mAppDiedCallback = null;
-        }
-    }
-
-    @GuardedBy("this")
-    void waitForProcKillLocked(final ProcessRecord app, final String formatString,
-            final long startTime) {
-        app.mAppDiedCallback = () -> {
-            synchronized (ActivityManagerService.this) {
-                // called when this app receives appDiedLocked()
-                ActivityManagerService.this.notifyAll();
-            }
-        };
-        checkTime(startTime, String.format(formatString, "before appDied"));
-        long now = SystemClock.uptimeMillis();
-        long timeout = PROC_KILL_TIMEOUT + now;
-        while (app.mAppDiedCallback != null && timeout > now) {
-            try {
-                wait(timeout - now);
-            } catch (InterruptedException e) {
-            }
-            now = SystemClock.uptimeMillis();
-        }
-        checkTime(startTime, String.format(formatString, "after appDied"));
-        if (app.mAppDiedCallback != null) {
-            Slog.w(TAG, String.format(formatString, "waiting for app killing timed out"));
-        }
-    }
-
     /**
      * If a stack trace dump file is configured, dump process stack traces.
      * @param firstPids of dalvik VM processes to dump stack traces for first
@@ -6824,12 +6780,18 @@
 
                 // Note if killedByAm is also set, this means the provider process has just been
                 // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
-                // yet. So we need to wait for appDiedLocked() here and let it clean up.
+                // yet. So we need to call appDiedLocked() here and let it clean up.
                 // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
                 // how to test this case.)
                 if (cpr.proc.killed && cpr.proc.killedByAm) {
-                    waitForProcKillLocked(cpr.proc, "getContentProviderImpl: %s (killedByAm)",
-                            startTime);
+                    checkTime(startTime, "getContentProviderImpl: before appDied (killedByAm)");
+                    final long iden = Binder.clearCallingIdentity();
+                    try {
+                        appDiedLocked(cpr.proc);
+                    } finally {
+                        Binder.restoreCallingIdentity(iden);
+                    }
+                    checkTime(startTime, "getContentProviderImpl: after appDied (killedByAm)");
                 }
             }
 
@@ -6933,7 +6895,9 @@
                     Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
                             + " is crashing; detaching " + r);
                     boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
-                    waitForProcKillLocked(cpr.proc, "getContentProviderImpl: %s", startTime);
+                    checkTime(startTime, "getContentProviderImpl: before appDied");
+                    appDiedLocked(cpr.proc);
+                    checkTime(startTime, "getContentProviderImpl: after appDied");
                     if (!lastRef) {
                         // This wasn't the last ref our process had on
                         // the provider...  we have now been killed, bail.
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 0524f91..638111e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -126,7 +126,7 @@
     // Keep the last WiFi stats so we can compute a delta.
     @GuardedBy("mWorkerLock")
     private WifiActivityEnergyInfo mLastInfo =
-            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
+            new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0);
 
     /**
      * Timestamp at which all external stats were last collected in
@@ -548,42 +548,45 @@
 
     @GuardedBy("mWorkerLock")
     private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
-        final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
-        final long lastScanMs = mLastInfo.mControllerScanTimeMs;
-        final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
-        final long lastTxMs = mLastInfo.mControllerTxTimeMs;
-        final long lastRxMs = mLastInfo.mControllerRxTimeMs;
-        final long lastEnergy = mLastInfo.mControllerEnergyUsed;
+        final long timePeriodMs = latest.getTimeSinceBootMillis()
+                - mLastInfo.getTimeSinceBootMillis();
+        final long lastScanMs = mLastInfo.getControllerScanDurationMillis();
+        final long lastIdleMs = mLastInfo.getControllerIdleDurationMillis();
+        final long lastTxMs = mLastInfo.getControllerTxDurationMillis();
+        final long lastRxMs = mLastInfo.getControllerRxDurationMillis();
+        final long lastEnergy = mLastInfo.getControllerEnergyUsedMicroJoules();
 
         // We will modify the last info object to be the delta, and store the new
         // WifiActivityEnergyInfo object as our last one.
         final WifiActivityEnergyInfo delta = mLastInfo;
-        delta.mTimestamp = latest.getTimeStamp();
-        delta.mStackState = latest.getStackState();
+        delta.setTimeSinceBootMillis(latest.getTimeSinceBootMillis());
+        delta.setStackState(latest.getStackState());
 
-        final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
-        final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
-        final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
-        final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
+        final long txTimeMs = latest.getControllerTxDurationMillis() - lastTxMs;
+        final long rxTimeMs = latest.getControllerRxDurationMillis() - lastRxMs;
+        final long idleTimeMs = latest.getControllerIdleDurationMillis() - lastIdleMs;
+        final long scanTimeMs = latest.getControllerScanDurationMillis() - lastScanMs;
 
         if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
             // The stats were reset by the WiFi system (which is why our delta is negative).
             // Returns the unaltered stats. The total on time should not exceed the time
             // duartion between reports.
-            final long totalOnTimeMs = latest.mControllerTxTimeMs + latest.mControllerRxTimeMs
-                        + latest.mControllerIdleTimeMs;
+            final long totalOnTimeMs = latest.getControllerTxDurationMillis()
+                    + latest.getControllerRxDurationMillis()
+                    + latest.getControllerIdleDurationMillis();
             if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
-                delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
-                delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
-                delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
-                delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
-                delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+                delta.setControllerEnergyUsedMicroJoules(
+                        latest.getControllerEnergyUsedMicroJoules());
+                delta.setControllerRxDurationMillis(latest.getControllerRxDurationMillis());
+                delta.setControllerTxDurationMillis(latest.getControllerTxDurationMillis());
+                delta.setControllerIdleDurationMillis(latest.getControllerIdleDurationMillis());
+                delta.setControllerScanDurationMillis(latest.getControllerScanDurationMillis());
             } else {
-                delta.mControllerEnergyUsed = 0;
-                delta.mControllerRxTimeMs = 0;
-                delta.mControllerTxTimeMs = 0;
-                delta.mControllerIdleTimeMs = 0;
-                delta.mControllerScanTimeMs = 0;
+                delta.setControllerEnergyUsedMicroJoules(0);
+                delta.setControllerRxDurationMillis(0);
+                delta.setControllerTxDurationMillis(0);
+                delta.setControllerIdleDurationMillis(0);
+                delta.setControllerScanDurationMillis(0);
             }
             Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
         } else {
@@ -608,28 +611,30 @@
                     sb.append(" e=").append(lastEnergy);
                     sb.append("\n");
                     sb.append("Current WiFi snapshot: ").append("idle=");
-                    TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
+                    TimeUtils.formatDuration(latest.getControllerIdleDurationMillis(), sb);
                     sb.append(" rx=");
-                    TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
+                    TimeUtils.formatDuration(latest.getControllerRxDurationMillis(), sb);
                     sb.append(" tx=");
-                    TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
-                    sb.append(" e=").append(latest.mControllerEnergyUsed);
+                    TimeUtils.formatDuration(latest.getControllerTxDurationMillis(), sb);
+                    sb.append(" e=").append(latest.getControllerEnergyUsedMicroJoules());
                     Slog.wtf(TAG, sb.toString());
                 }
             } else {
                 maxExpectedIdleTimeMs = timePeriodMs - totalActiveTimeMs;
             }
             // These times seem to be the most reliable.
-            delta.mControllerTxTimeMs = txTimeMs;
-            delta.mControllerRxTimeMs = rxTimeMs;
-            delta.mControllerScanTimeMs = scanTimeMs;
+            delta.setControllerTxDurationMillis(txTimeMs);
+            delta.setControllerRxDurationMillis(rxTimeMs);
+            delta.setControllerScanDurationMillis(scanTimeMs);
             // WiFi calculates the idle time as a difference from the on time and the various
             // Rx + Tx times. There seems to be some missing time there because this sometimes
             // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
             // time from the difference in timestamps.
             // b/21613534
-            delta.mControllerIdleTimeMs = Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs));
-            delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
+            delta.setControllerIdleDurationMillis(
+                    Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs)));
+            delta.setControllerEnergyUsedMicroJoules(
+                    Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy));
         }
 
         mLastInfo = latest;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 375da8a..2bb7035 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1421,7 +1421,7 @@
         if (app.pendingStart) {
             return true;
         }
-        long startTime = SystemClock.uptimeMillis();
+        long startTime = SystemClock.elapsedRealtime();
         if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
             checkSlow(startTime, "startProcess: removing from pids map");
             mService.mPidsSelfLocked.remove(app);
@@ -1856,7 +1856,7 @@
             boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
             boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
             String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
-        long startTime = SystemClock.uptimeMillis();
+        long startTime = SystemClock.elapsedRealtime();
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
@@ -1917,9 +1917,10 @@
             // An application record is attached to a previous process,
             // clean it up now.
             if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
+            checkSlow(startTime, "startProcess: bad proc running, killing");
             ProcessList.killProcessGroup(app.uid, app.pid);
-            mService.waitForProcKillLocked(app, "startProcess: bad proc running, killing: %s",
-                    startTime);
+            mService.handleAppDiedLocked(app, true, true);
+            checkSlow(startTime, "startProcess: done killing old proc");
         }
 
         if (app == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 867571a..5db6ff7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -329,9 +329,6 @@
 
     boolean mReachable; // Whether or not this process is reachable from given process
 
-    // A callback that should be executed on app died; after that it'll be set to null
-    Runnable mAppDiedCallback;
-
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
         this.startUid = startUid;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7e9a17b..366766e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1115,6 +1115,7 @@
                     // There is some actively running operation...  need to find it
                     // and appropriately update its state.
                     final long now = System.currentTimeMillis();
+                    final long nowElapsed = SystemClock.elapsedRealtime();
                     for (int i = uidState.pkgOps.size() - 1; i >= 0; i--) {
                         final Ops ops = uidState.pkgOps.valueAt(i);
                         for (int j = ops.size() - 1; j >= 0; j--) {
@@ -1136,7 +1137,7 @@
                                     featureOp.finished(now, duration, oldPendingState,
                                             AppOpsManager.OP_FLAG_SELF);
                                     // Start the op in the new state
-                                    featureOp.startRealtime = now;
+                                    featureOp.startRealtime = nowElapsed;
                                     featureOp.started(now, newState, AppOpsManager.OP_FLAG_SELF);
                                 }
                             }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e426c6c..4bf1de6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -269,6 +269,7 @@
     private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
     private static final int MSG_HDMI_VOLUME_CHECK = 28;
     private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
+    private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -2925,9 +2926,8 @@
             AudioSystem.muteMicrophone(muted);
             try {
                 if (muted != currentMute) {
-                    mContext.sendBroadcastAsUser(
-                        new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
-                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+                    sendMsg(mAudioHandler, MSG_BROADCAST_MICROPHONE_MUTE,
+                                SENDMSG_NOOP, 0, 0, null, 0);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -5236,6 +5236,13 @@
                 case MSG_PLAYBACK_CONFIG_CHANGE:
                     onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
                     break;
+
+                case MSG_BROADCAST_MICROPHONE_MUTE:
+                    mContext.sendBroadcastAsUser(
+                            new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
+                                    .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                                    UserHandle.ALL);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 79b56c6..33f6ed5 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,6 +20,7 @@
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
+import android.net.NetworkStack;
 import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.os.Binder;
@@ -276,7 +277,7 @@
 
         @Override
         public int logEvent(ConnectivityMetricsEvent event) {
-            enforceConnectivityInternalPermission();
+            NetworkStack.checkNetworkStackPermission(getContext());
             return append(event);
         }
 
@@ -299,10 +300,6 @@
             }
         }
 
-        private void enforceConnectivityInternalPermission() {
-            enforcePermission(android.Manifest.permission.CONNECTIVITY_INTERNAL);
-        }
-
         private void enforceDumpPermission() {
             enforcePermission(android.Manifest.permission.DUMP);
         }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 56f4959..f0b7150 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -17,7 +17,6 @@
 package com.android.server.connectivity;
 
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.INTERNET;
 import static android.Manifest.permission.NETWORK_STACK;
@@ -25,6 +24,7 @@
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
@@ -259,7 +259,8 @@
                 return true;
             }
         }
-        return hasPermission(app, CONNECTIVITY_INTERNAL)
+
+        return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
                 || hasPermission(app, NETWORK_STACK)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
     }
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9882f6c..7ce63c5 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -138,13 +138,17 @@
     }
 
     /**
-     * Sets the display modes the system is allowed to switch between, roughly ordered by
-     * preference.
+     * Sets the refresh ranges, and display modes that the system is allowed to switch between.
+     * Display modes are roughly ordered by preference.
      *
      * Not all display devices will automatically switch between modes, so it's important that the
      * most-desired modes are at the beginning of the allowed array.
+     *
+     * @param defaultModeId is used, if the device does not support multiple refresh
+     * rates, and to navigate other parameters.
      */
-    public void setAllowedDisplayModesLocked(int[] modes) {
+    public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+            float maxRefreshRate, int[] modes) {
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c466640..d20191d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -431,7 +431,7 @@
             recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
         }
 
-        mDisplayModeDirector.setListener(new AllowedDisplayModeObserver());
+        mDisplayModeDirector.setDisplayModeListener(new AllowedDisplayModeObserver());
         mDisplayModeDirector.start(mSensorManager);
 
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -2488,7 +2488,7 @@
 
     }
 
-    class AllowedDisplayModeObserver implements DisplayModeDirector.Listener {
+    class AllowedDisplayModeObserver implements DisplayModeDirector.DisplayModeListener {
         public void onAllowedDisplayModesChanged() {
             onAllowedDisplayModesChangedInternal();
         }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index d24bd1a..2df682f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -18,26 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-
+import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -47,11 +43,11 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.display.utils.AmbientFilter;
 import com.android.server.display.utils.AmbientFilterFactory;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -87,11 +83,11 @@
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
     // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
-    private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
+    private SparseArray<SparseArray<Vote>> mVotesByDisplay;
     // A map from the display ID to the supported modes on that display.
-    private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
+    private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
     // A map from the display ID to the default mode of that display.
-    private final SparseArray<Display.Mode> mDefaultModeByDisplay;
+    private SparseArray<Display.Mode> mDefaultModeByDisplay;
 
     private final AppRequestObserver mAppRequestObserver;
     private final SettingsObserver mSettingsObserver;
@@ -99,7 +95,7 @@
     private final BrightnessObserver mBrightnessObserver;
 
     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
-    private Listener mListener;
+    private DisplayModeListener mDisplayModeListener;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
         mContext = context;
@@ -143,17 +139,7 @@
      */
     @NonNull
     public int[] getAllowedModes(int displayId) {
-        synchronized (mLock) {
-            SparseArray<Vote> votes = getVotesLocked(displayId);
-            Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
-            Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
-            if (modes == null || defaultMode == null) {
-                Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
-                        + displayId + ")");
-                return new int[0];
-            }
-            return getAllowedModesLocked(votes, modes, defaultMode);
-        }
+        return getDesiredDisplayConfigSpecs(displayId).allowedConfigs;
     }
 
     @NonNull
@@ -178,76 +164,101 @@
         return votes;
     }
 
+    /**
+     * Calculates the refresh rate ranges and display modes that the system is allowed to freely
+     * switch between based on global and display-specific constraints.
+     *
+     * @param displayId The display to query for.
+     * @return The ID of the default mode the system should use, and the refresh rate range the
+     * system is allowed to switch between.
+     */
     @NonNull
-    private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
-            @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
-        int lowestConsideredPriority = Vote.MIN_PRIORITY;
-        while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+    public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) {
+        synchronized (mLock) {
+            SparseArray<Vote> votes = getVotesLocked(displayId);
+            Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+            Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
+            if (modes == null || defaultMode == null) {
+                Slog.e(TAG, "Asked about unknown display, returning empty desired configs!"
+                        + "(id=" + displayId + ")");
+                return new DesiredDisplayConfigSpecs(displayId, new RefreshRateRange(60, 60),
+                        new int[0]);
+            }
+
+            int[] availableModes = new int[]{defaultMode.getModeId()};
             float minRefreshRate = 0f;
             float maxRefreshRate = Float.POSITIVE_INFINITY;
-            int height = Vote.INVALID_SIZE;
-            int width = Vote.INVALID_SIZE;
+            int lowestConsideredPriority = Vote.MIN_PRIORITY;
+            while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+                int height = Vote.INVALID_SIZE;
+                int width = Vote.INVALID_SIZE;
 
-            for (int priority = Vote.MAX_PRIORITY;
-                    priority >= lowestConsideredPriority;
-                    priority--) {
-                Vote vote = votes.get(priority);
-                if (vote == null) {
-                    continue;
+                for (int priority = Vote.MAX_PRIORITY;
+                        priority >= lowestConsideredPriority; priority--) {
+                    Vote vote = votes.get(priority);
+                    if (vote == null) {
+                        continue;
+                    }
+                    // For refresh rates, just use the tightest bounds of all the votes
+                    minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min);
+                    maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max);
+                    // For display size, use only the first vote we come across (i.e. the highest
+                    // priority vote that includes the width / height).
+                    if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
+                            && vote.height > 0 && vote.width > 0) {
+                        width = vote.width;
+                        height = vote.height;
+                    }
                 }
-                // For refresh rates, just use the tightest bounds of all the votes
-                minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
-                maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
-                // For display size, use only the first vote we come across (i.e. the highest
-                // priority vote that includes the width / height).
-                if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
-                        && vote.height > 0 && vote.width > 0) {
-                    width = vote.width;
-                    height = vote.height;
+
+                // If we don't have anything specifying the width / height of the display, just use
+                // the default width and height. We don't want these switching out from underneath
+                // us since it's a pretty disruptive behavior.
+                if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
+                    width = defaultMode.getPhysicalWidth();
+                    height = defaultMode.getPhysicalHeight();
                 }
-            }
 
-            // If we don't have anything specifying the width / height of the display, just use the
-            // default width and height. We don't want these switching out from underneath us since
-            // it's a pretty disruptive behavior.
-            if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
-                width = defaultMode.getPhysicalWidth();
-                height = defaultMode.getPhysicalHeight();
-            }
+                availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
+                if (availableModes.length > 0) {
+                    if (DEBUG) {
+                        Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+                                + " with lowest priority considered "
+                                + Vote.priorityToString(lowestConsideredPriority)
+                                + " and constraints: "
+                                + "width=" + width
+                                + ", height=" + height
+                                + ", minRefreshRate=" + minRefreshRate
+                                + ", maxRefreshRate=" + maxRefreshRate);
+                    }
+                    break;
+                }
 
-            int[] availableModes =
-                    filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
-            if (availableModes.length > 0) {
                 if (DEBUG) {
-                    Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
-                            + " with lowest priority considered "
+                    Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
                             + Vote.priorityToString(lowestConsideredPriority)
-                            + " and constraints: "
+                            + " and with the following constraints: "
                             + "width=" + width
                             + ", height=" + height
                             + ", minRefreshRate=" + minRefreshRate
                             + ", maxRefreshRate=" + maxRefreshRate);
                 }
-                return availableModes;
+
+                // If we haven't found anything with the current set of votes, drop the
+                // current lowest priority vote.
+                lowestConsideredPriority++;
             }
 
-            if (DEBUG) {
-                Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
-                        + Vote.priorityToString(lowestConsideredPriority)
-                        + " and with the following constraints: "
-                        + "width=" + width
-                        + ", height=" + height
-                        + ", minRefreshRate=" + minRefreshRate
-                        + ", maxRefreshRate=" + maxRefreshRate);
+            int defaultModeId = defaultMode.getModeId();
+            if (availableModes.length > 0) {
+                defaultModeId = availableModes[0];
             }
-            // If we haven't found anything with the current set of votes, drop the current lowest
-            // priority vote.
-            lowestConsideredPriority++;
+            // filterModes function is going to filter the modes based on the voting system. If
+            // the application requests a given mode with preferredModeId function, it will be
+            // stored as the first and only element in available modes array.
+            return new DesiredDisplayConfigSpecs(defaultModeId,
+                    new RefreshRateRange(minRefreshRate, maxRefreshRate), availableModes);
         }
-
-        // If we still haven't found anything that matches our current set of votes, just fall back
-        // to the default mode.
-        return new int[] { defaultMode.getModeId() };
     }
 
     private int[] filterModes(Display.Mode[] supportedModes,
@@ -300,11 +311,11 @@
     }
 
     /**
-     * Sets the listener for changes to allowed display modes.
+     * Sets the modeListener for changes to allowed display modes.
      */
-    public void setListener(@Nullable Listener listener) {
+    public void setDisplayModeListener(@Nullable DisplayModeListener displayModeListener) {
         synchronized (mLock) {
-            mListener = listener;
+            mDisplayModeListener = displayModeListener;
         }
     }
 
@@ -382,12 +393,12 @@
     }
 
     private void notifyAllowedModesChangedLocked() {
-        if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
+        if (mDisplayModeListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
             // We need to post this to a handler to avoid calling out while holding the lock
             // since we know there are things that both listen for changes as well as provide
             // information. If we did call out while holding the lock, then there's no guaranteed
             // lock order and we run the real of risk deadlock.
-            Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
+            Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mDisplayModeListener);
             msg.sendToTarget();
         }
     }
@@ -403,10 +414,25 @@
         }
     }
 
+    @VisibleForTesting
+    void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
+        mSupportedModesByDisplay = supportedModesByDisplay;
+    }
+
+    @VisibleForTesting
+    void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
+        mDefaultModeByDisplay = defaultModeByDisplay;
+    }
+
+    @VisibleForTesting
+    void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
+        mVotesByDisplay = votesByDisplay;
+    }
+
     /**
      * Listens for changes to display mode coordination.
      */
-    public interface Listener {
+    public interface DisplayModeListener {
         /**
          * Called when the allowed display modes may have changed.
          */
@@ -422,8 +448,8 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_ALLOWED_MODES_CHANGED:
-                    Listener listener = (Listener) msg.obj;
-                    listener.onAllowedDisplayModesChanged();
+                    DisplayModeListener displayModeListener = (DisplayModeListener) msg.obj;
+                    displayModeListener.onAllowedDisplayModesChanged();
                     break;
 
                 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
@@ -452,7 +478,129 @@
         }
     }
 
-    private static final class Vote {
+    /**
+     * Information about the min and max refresh rate DM would like to set the display to.
+     */
+    public static final class RefreshRateRange {
+        /**
+         * The lowest desired refresh rate.
+         */
+        public final float min;
+        /**
+         * The highest desired refresh rate.
+         */
+        public final float max;
+
+        public RefreshRateRange(float min, float max) {
+            if (min < 0 || max < 0 || min > max) {
+                Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+                        + min + " " + max);
+                this.min = this.max = 0;
+                return;
+            }
+            this.min = min;
+            this.max = max;
+        }
+
+        /**
+         * Checks whether the two objects have the same values.
+         */
+        @Override
+        public boolean equals(Object other) {
+            if (other == this) {
+                return true;
+            }
+
+            if (!(other instanceof RefreshRateRange)) {
+                return false;
+            }
+
+            RefreshRateRange refreshRateRange = (RefreshRateRange) other;
+            return (min == refreshRateRange.min && max == refreshRateRange.max);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(min, max);
+        }
+
+        @Override
+        public String toString() {
+            return "(" + min + " " + max + ")";
+        }
+    }
+
+    /**
+     * Information about the desired configuration to be set by the system. Includes the default
+     * configuration ID, refresh rate range, and the list of policy decisions that influenced the
+     * choice.
+     */
+    public static final class DesiredDisplayConfigSpecs {
+        /**
+         * Default configuration ID. This is what system defaults to for all other settings, or
+         * if the refresh rate range is not available.
+         */
+        public final int defaultModeId;
+        /**
+         * The refresh rate range.
+         */
+        public final RefreshRateRange refreshRateRange;
+        /**
+         * For legacy reasons, keep a list of allowed configs.
+         * TODO(b/142507213): Re-assess whether the list of allowed configs is still necessary.
+         */
+        public final int[] allowedConfigs;
+
+        public DesiredDisplayConfigSpecs(int defaultModeId,
+                @NonNull RefreshRateRange refreshRateRange,
+                @NonNull int[] allowedConfigs) {
+            this.defaultModeId = defaultModeId;
+            this.refreshRateRange = refreshRateRange;
+            this.allowedConfigs = allowedConfigs;
+        }
+
+        /**
+         * Returns a string representation of the object.
+         */
+        @Override
+        public String toString() {
+            return "DesiredDisplayConfigSpecs(defaultModeId=" + defaultModeId
+                    + ", refreshRateRange=" + refreshRateRange.toString()
+                    + ", allowedConfigs=" + Arrays.toString(allowedConfigs) + ")";
+        }
+        /**
+         * Checks whether the two objects have the same values.
+         */
+        @Override
+        public boolean equals(Object other) {
+            if (other == this) {
+                return true;
+            }
+
+            if (!(other instanceof DesiredDisplayConfigSpecs)) {
+                return false;
+            }
+
+            DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
+                    (DesiredDisplayConfigSpecs) other;
+
+            if (defaultModeId != desiredDisplayConfigSpecs.defaultModeId) {
+                return false;
+            }
+            if (!refreshRateRange.equals(desiredDisplayConfigSpecs.refreshRateRange)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(defaultModeId, refreshRateRange);
+        }
+    }
+
+    @VisibleForTesting
+    static final class Vote {
         // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
         // If the higher voters result is a range, it will fix the rate to a single choice.
         // It's used to avoid rate switch in certain conditions.
@@ -499,15 +647,10 @@
          * The requested height of the display in pixels, or INVALID_SIZE;
          */
         public final int height;
-
         /**
-         * The lowest desired refresh rate.
+         * Information about the min and max refresh rate DM would like to set the display to.
          */
-        public final float minRefreshRate;
-        /**
-         * The highest desired refresh rate.
-         */
-        public final float maxRefreshRate;
+        public final RefreshRateRange refreshRateRange;
 
         public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
             return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
@@ -521,8 +664,8 @@
                 float minRefreshRate, float maxRefreshRate) {
             this.width = width;
             this.height = height;
-            this.minRefreshRate = minRefreshRate;
-            this.maxRefreshRate = maxRefreshRate;
+            this.refreshRateRange =
+                    new RefreshRateRange(minRefreshRate, maxRefreshRate);
         }
 
         public static String priorityToString(int priority) {
@@ -547,11 +690,9 @@
         @Override
         public String toString() {
             return "Vote{"
-                + "width=" + width
-                + ", height=" + height
-                + ", minRefreshRate=" + minRefreshRate
-                + ", maxRefreshRate=" + maxRefreshRate
-                + "}";
+                + "width=" + width + ", height=" + height
+                + ", minRefreshRate=" + refreshRateRange.min
+                + ", maxRefreshRate=" + refreshRateRange.max + "}";
         }
     }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b03dc3b..c4ea81a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -173,6 +173,8 @@
         private int mActiveModeId;
         private boolean mActiveModeInvalid;
         private int[] mAllowedModeIds;
+        private float mMinRefreshRate;
+        private float mMaxRefreshRate;
         private boolean mAllowedModeIdsInvalid;
         private int mActivePhysIndex;
         private int[] mAllowedPhysIndexes;
@@ -623,7 +625,9 @@
         }
 
         @Override
-        public void setAllowedDisplayModesLocked(int[] modes) {
+        public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+                float maxRefreshRate, int[] modes) {
+            updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate);
             updateAllowedModesLocked(modes);
         }
 
@@ -653,6 +657,7 @@
             return true;
         }
 
+        // TODO(b/142507213): Remove once refresh rates are plummed through to kernel.
         public void updateAllowedModesLocked(int[] allowedModes) {
             if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) {
                 return;
@@ -662,6 +667,39 @@
             }
         }
 
+        public void updateDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+                float maxRefreshRate) {
+            if (minRefreshRate == mMinRefreshRate
+                        && maxRefreshRate == mMaxRefreshRate
+                        && defaultModeId == mDefaultModeId) {
+                return;
+            }
+            if (updateDesiredDisplayConfigSpecsInternalLocked(defaultModeId, minRefreshRate,
+                    maxRefreshRate)) {
+                updateDeviceInfoLocked();
+            }
+        }
+
+        public boolean updateDesiredDisplayConfigSpecsInternalLocked(int defaultModeId,
+                float minRefreshRate, float maxRefreshRate) {
+            if (DEBUG) {
+                Slog.w(TAG, "updateDesiredDisplayConfigSpecsInternalLocked("
+                        + "defaultModeId="
+                        + Integer.toString(defaultModeId)
+                        + ", minRefreshRate="
+                        + Float.toString(minRefreshRate)
+                        + ", maxRefreshRate="
+                        + Float.toString(minRefreshRate));
+            }
+
+            final IBinder token = getDisplayTokenLocked();
+            SurfaceControl.setDesiredDisplayConfigSpecs(token,
+                    new SurfaceControl.DesiredDisplayConfigSpecs(
+                            defaultModeId, minRefreshRate, maxRefreshRate));
+            int activePhysIndex = SurfaceControl.getActiveConfig(token);
+            return updateActiveModeLocked(activePhysIndex);
+        }
+
         public boolean updateAllowedModesInternalLocked(int[] allowedModes) {
             if (DEBUG) {
                 Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes="
@@ -735,6 +773,8 @@
             pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
             pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes));
             pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds));
+            pw.println("mMinRefreshRate=" + mMinRefreshRate);
+            pw.println("mMaxRefreshRate=" + mMaxRefreshRate);
             pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid);
             pw.println("mActivePhysIndex=" + mActivePhysIndex);
             pw.println("mActiveModeId=" + mActiveModeId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index dcef998..f4b2dc8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -352,11 +352,12 @@
 
         // Set the color mode and allowed display mode.
         if (device == mPrimaryDisplayDevice) {
-            device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
+            // See ag/9588196 for correct values.
+            device.setDesiredDisplayConfigSpecs(0, 60, 60, mAllowedDisplayModes);
             device.setRequestedColorModeLocked(mRequestedColorMode);
         } else {
             // Reset to default for non primary displays
-            device.setAllowedDisplayModesLocked(new int[] {0});
+            device.setDesiredDisplayConfigSpecs(0, 60, 60, new int[] {0});
             device.setRequestedColorModeLocked(0);
         }
 
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 60cfbd0..739dd64 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -315,16 +315,9 @@
         }
 
         @Override
-        public void setAllowedDisplayModesLocked(int[] modes) {
-            final int id;
-            if (modes.length > 0) {
-                // The allowed modes should be ordered by preference, so just use the first mode
-                // here.
-                id = modes[0];
-            } else {
-                // If we don't have any allowed modes, just use the default mode.
-                id = 0;
-            }
+        public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+                float maxRefreshRate, int[] modes) {
+            final int id = defaultModeId;
             int index = -1;
             if (id == 0) {
                 // Use the default.
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 111b95a..aad177e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -25,7 +25,7 @@
 public class RuleBinaryParser implements RuleParser {
 
     @Override
-    public List<Rule> parse(String ruleText) {
+    public List<Rule> parse(byte[] ruleBytes) {
         // TODO: Implement binary text parser.
         return null;
     }
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java
index 4e1f914..81783d5 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java
@@ -24,8 +24,8 @@
 /** A helper class to parse rules into the {@link Rule} model. */
 public interface RuleParser {
 
-    /** Parse rules from a string. */
-    List<Rule> parse(String ruleText) throws RuleParseException;
+    /** Parse rules from bytes. */
+    List<Rule> parse(byte[] ruleBytes) throws RuleParseException;
 
     /** Parse rules from an input stream. */
     List<Rule> parse(InputStream inputStream) throws RuleParseException;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
index 1212a08..2e99d0f 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -51,10 +51,10 @@
     private static final String IS_HASHED_VALUE_ATTRIBUTE = "H";
 
     @Override
-    public List<Rule> parse(String ruleText) throws RuleParseException {
+    public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
         try {
             XmlPullParser xmlPullParser = Xml.newPullParser();
-            xmlPullParser.setInput(new StringReader(ruleText));
+            xmlPullParser.setInput(new StringReader(new String(ruleBytes, StandardCharsets.UTF_8)));
             return parseRules(xmlPullParser);
         } catch (Exception e) {
             throw new RuleParseException(e.getMessage(), e);
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
index dc5628e..eb2a37b 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -33,21 +33,51 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemConfig;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Supplier;
 
 /**
  * Provides accessors and listeners for all location related settings.
  */
 public class LocationSettingsStore {
 
+    /**
+     * Listener for user-specific settings changes.
+     */
+    public interface UserSettingChangedListener {
+        /**
+         * Called when setting changes.
+         */
+        void onSettingChanged(int userId);
+    }
+
+    /**
+     * Listener for global settings changes.
+     */
+    public interface GlobalSettingChangedListener extends UserSettingChangedListener {
+        /**
+         * Called when setting changes.
+         */
+        void onSettingChanged();
+
+        @Override
+        default void onSettingChanged(int userId) {
+            onSettingChanged();
+        }
+    }
+
     private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
     private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
 
@@ -63,9 +93,10 @@
     private final LongGlobalSetting mBackgroundThrottleIntervalMs;
     private final StringListCachedSecureSetting mLocationPackageBlacklist;
     private final StringListCachedSecureSetting mLocationPackageWhitelist;
-    private final StringListCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
-    private final StringListCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
+    private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
+    private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
 
+    // TODO: get rid of handler
     public LocationSettingsStore(Context context, Handler handler) {
         mContext = context;
 
@@ -78,10 +109,12 @@
                 LOCATION_PACKAGE_BLACKLIST, handler);
         mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
                 LOCATION_PACKAGE_WHITELIST, handler);
-        mBackgroundThrottlePackageWhitelist = new StringListCachedGlobalSetting(context,
-                LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, handler);
-        mIgnoreSettingsPackageWhitelist = new StringListCachedGlobalSetting(context,
-                LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, handler);
+        mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context,
+                LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), handler);
+        mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context,
+                LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
+                () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler);
     }
 
     /**
@@ -94,14 +127,14 @@
     /**
      * Add a listener for changes to the location enabled setting.
      */
-    public void addOnLocationEnabledChangedListener(Runnable listener) {
+    public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
         mLocationMode.addListener(listener);
     }
 
     /**
      * Remove a listener for changes to the location enabled setting.
      */
-    public void removeOnLocationEnabledChangedListener(Runnable listener) {
+    public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
         mLocationMode.addListener(listener);
     }
 
@@ -115,15 +148,16 @@
     /**
      * Add a listener for changes to the currently allowed location providers.
      */
-    public void addOnLocationProvidersAllowedChangedListener(Runnable runnable) {
-        mLocationProvidersAllowed.addListener(runnable);
+    public void addOnLocationProvidersAllowedChangedListener(UserSettingChangedListener listener) {
+        mLocationProvidersAllowed.addListener(listener);
     }
 
     /**
      * Remove a listener for changes to the currently allowed location providers.
      */
-    public void removeOnLocationProvidersAllowedChangedListener(Runnable runnable) {
-        mLocationProvidersAllowed.removeListener(runnable);
+    public void removeOnLocationProvidersAllowedChangedListener(
+            UserSettingChangedListener listener) {
+        mLocationProvidersAllowed.removeListener(listener);
     }
 
     /**
@@ -136,14 +170,16 @@
     /**
      * Add a listener for changes to the background throttle interval.
      */
-    public void addOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
+    public void addOnBackgroundThrottleIntervalChangedListener(
+            GlobalSettingChangedListener listener) {
         mBackgroundThrottleIntervalMs.addListener(listener);
     }
 
     /**
      * Remove a listener for changes to the background throttle interval.
      */
-    public void removeOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
+    public void removeOnBackgroundThrottleIntervalChangedListener(
+            GlobalSettingChangedListener listener) {
         mBackgroundThrottleIntervalMs.removeListener(listener);
     }
 
@@ -175,42 +211,46 @@
     /**
      * Retrieve the background throttle package whitelist.
      */
-    public List<String> getBackgroundThrottlePackageWhitelist() {
+    public Set<String> getBackgroundThrottlePackageWhitelist() {
         return mBackgroundThrottlePackageWhitelist.getValue();
     }
 
     /**
      * Add a listener for changes to the background throttle package whitelist.
      */
-    public void addOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
+    public void addOnBackgroundThrottlePackageWhitelistChangedListener(
+            GlobalSettingChangedListener listener) {
         mBackgroundThrottlePackageWhitelist.addListener(listener);
     }
 
     /**
      * Remove a listener for changes to the background throttle package whitelist.
      */
-    public void removeOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
+    public void removeOnBackgroundThrottlePackageWhitelistChangedListener(
+            GlobalSettingChangedListener listener) {
         mBackgroundThrottlePackageWhitelist.removeListener(listener);
     }
 
     /**
      * Retrieve the ignore settings package whitelist.
      */
-    public List<String> getIgnoreSettingsPackageWhitelist() {
+    public Set<String> getIgnoreSettingsPackageWhitelist() {
         return mIgnoreSettingsPackageWhitelist.getValue();
     }
 
     /**
      * Add a listener for changes to the ignore settings package whitelist.
      */
-    public void addOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
+    public void addOnIgnoreSettingsPackageWhitelistChangedListener(
+            GlobalSettingChangedListener listener) {
         mIgnoreSettingsPackageWhitelist.addListener(listener);
     }
 
     /**
      * Remove a listener for changes to the ignore settings package whitelist.
      */
-    public void removeOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
+    public void removeOnIgnoreSettingsPackageWhitelistChangedListener(
+            GlobalSettingChangedListener listener) {
         mIgnoreSettingsPackageWhitelist.removeListener(listener);
     }
 
@@ -264,7 +304,7 @@
             }
         }
 
-        List<String> backgroundThrottlePackageWhitelist =
+        Set<String> backgroundThrottlePackageWhitelist =
                 mBackgroundThrottlePackageWhitelist.getValue();
         if (!backgroundThrottlePackageWhitelist.isEmpty()) {
             ipw.println("Throttling Whitelisted Packages:");
@@ -275,7 +315,7 @@
             ipw.decreaseIndent();
         }
 
-        List<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
+        Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
         if (!ignoreSettingsPackageWhitelist.isEmpty()) {
             ipw.println("Bypass Whitelisted Packages:");
             ipw.increaseIndent();
@@ -288,7 +328,7 @@
 
     private abstract static class ObservingSetting extends ContentObserver {
 
-        private final CopyOnWriteArrayList<Runnable> mListeners;
+        private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
 
         private ObservingSetting(Context context, String settingName, Handler handler) {
             super(handler);
@@ -298,11 +338,11 @@
                     getUriFor(settingName), false, this, UserHandle.USER_ALL);
         }
 
-        public void addListener(Runnable listener) {
+        public void addListener(UserSettingChangedListener listener) {
             mListeners.add(listener);
         }
 
-        public void removeListener(Runnable listener) {
+        public void removeListener(UserSettingChangedListener listener) {
             mListeners.remove(listener);
         }
 
@@ -310,8 +350,8 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
-            for (Runnable listener : mListeners) {
-                listener.run();
+            for (UserSettingChangedListener listener : mListeners) {
+                listener.onSettingChanged(userId);
             }
         }
     }
@@ -354,6 +394,8 @@
         }
 
         public synchronized List<String> getValueForUser(int userId) {
+            Preconditions.checkArgument(userId != UserHandle.USER_NULL);
+
             if (userId != mCachedUserId) {
                 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                         mSettingName, userId);
@@ -409,29 +451,30 @@
         }
     }
 
-    private static class StringListCachedGlobalSetting extends ObservingSetting {
+    private static class StringSetCachedGlobalSetting extends ObservingSetting {
 
         private final Context mContext;
         private final String mSettingName;
+        private final Supplier<ArraySet<String>> mBaseValuesSupplier;
 
         private boolean mValid;
-        private List<String> mCachedValue;
+        private ArraySet<String> mCachedValue;
 
-        private StringListCachedGlobalSetting(Context context, String settingName,
-                Handler handler) {
+        private StringSetCachedGlobalSetting(Context context, String settingName,
+                Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) {
             super(context, settingName, handler);
             mContext = context;
             mSettingName = settingName;
+            mBaseValuesSupplier = baseValuesSupplier;
         }
 
-        public synchronized List<String> getValue() {
+        public synchronized Set<String> getValue() {
             if (!mValid) {
+                mCachedValue = new ArraySet<>(mBaseValuesSupplier.get());
                 String setting = Settings.Global.getString(mContext.getContentResolver(),
                         mSettingName);
-                if (TextUtils.isEmpty(setting)) {
-                    mCachedValue = Collections.emptyList();
-                } else {
-                    mCachedValue = Arrays.asList(setting.split(","));
+                if (!TextUtils.isEmpty(setting)) {
+                    mCachedValue.addAll(Arrays.asList(setting.split(",")));
                 }
                 mValid = true;
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a4e7ac4..83b6215 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -181,6 +181,7 @@
     private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
     private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
     private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
+    private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
 
     // No challenge provided
     private static final int CHALLENGE_NONE = 0;
@@ -661,6 +662,34 @@
     }
 
     /**
+     * Clean up states associated with the given user, in case the userId is reused but LSS didn't
+     * get a chance to do cleanup previously during ACTION_USER_REMOVED.
+     *
+     * Internally, LSS stores serial number for each user and check it against the current user's
+     * serial number to determine if the userId is reused and invoke cleanup code.
+     */
+    private void cleanupDataForReusedUserIdIfNecessary(int userId) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            // Short circuit as we never clean up user 0.
+            return;
+        }
+        // Serial number is never reusued, so we can use it as a distinguisher for user Id reuse.
+        int serialNumber = mUserManager.getUserSerialNumber(userId);
+
+        int storedSerialNumber = getIntUnchecked(USER_SERIAL_NUMBER_KEY, -1, userId);
+        if (storedSerialNumber != serialNumber) {
+            // If LockSettingsStorage does not have a copy of the serial number, it could be either
+            // this is a user created before the serial number recording logic is introduced, or
+            // the user does not exist or was removed and cleaned up properly. In either case, don't
+            // invoke removeUser().
+            if (storedSerialNumber != -1) {
+                removeUser(userId, /* unknownUser */ true);
+            }
+            setIntUnchecked(USER_SERIAL_NUMBER_KEY, serialNumber, userId);
+        }
+    }
+
+    /**
      * Check if profile got unlocked but the keystore is still locked. This happens on full disk
      * encryption devices since the profile may not yet be running when we consider unlocking it
      * during the normal flow. In this case unlock the keystore for the profile.
@@ -684,6 +713,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
+                cleanupDataForReusedUserIdIfNecessary(userId);
                 ensureProfileKeystoreUnlocked(userId);
                 // Hide notification first, as tie managed profile lock takes time
                 hideEncryptionNotification(new UserHandle(userId));
@@ -729,9 +759,6 @@
             if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
                 // Notify keystore that a new user was added.
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                if (userHandle > UserHandle.USER_SYSTEM) {
-                    removeUser(userHandle, /* unknownUser= */ true);
-                }
                 final KeyStore ks = KeyStore.getInstance();
                 final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
                 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
@@ -1066,6 +1093,10 @@
         setStringUnchecked(key, userId, Long.toString(value));
     }
 
+    private void setIntUnchecked(String key, int value, int userId) {
+        setStringUnchecked(key, userId, Integer.toString(value));
+    }
+
     @Override
     public void setString(String key, String value, int userId) {
         checkWritePermission(userId);
@@ -1104,6 +1135,11 @@
         return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
     }
 
+    private int getIntUnchecked(String key, int defaultValue, int userId) {
+        String value = getStringUnchecked(key, null, userId);
+        return TextUtils.isEmpty(value) ? defaultValue : Integer.parseInt(value);
+    }
+
     @Override
     public String getString(String key, String defaultValue, int userId) {
         checkReadPermission(key, userId);
@@ -2171,8 +2207,8 @@
     }
 
     private void removeUser(int userId, boolean unknownUser) {
+        Slog.i(TAG, "RemoveUser: " + userId);
         mSpManager.removeUser(userId);
-        mStorage.removeUser(userId);
         mStrongAuth.removeUser(userId);
         tryRemoveUserFromSpCacheLater(userId);
 
@@ -2183,6 +2219,9 @@
         if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
             removeKeystoreProfileKey(userId);
         }
+        // Clean up storage last, this is to ensure that cleanupDataForReusedUserIdIfNecessary()
+        // can make the assumption that no USER_SERIAL_NUMBER_KEY means user is fully removed.
+        mStorage.removeUser(userId);
     }
 
     private void removeKeystoreProfileKey(int targetUserId) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 2c478df..9fcee50 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageManager;
 import android.media.IMediaRouter2Client;
 import android.media.IMediaRouter2Manager;
-import android.media.IMediaRouterClient;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRouter2;
@@ -71,7 +70,7 @@
     @GuardedBy("mLock")
     private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
     @GuardedBy("mLock")
-    private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>();
+    private final ArrayMap<IBinder, Client2Record> mAllClientRecords = new ArrayMap<>();
     @GuardedBy("mLock")
     private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>();
     @GuardedBy("mLock")
@@ -183,29 +182,14 @@
         }
     }
 
-    //TODO: What would happen if a media app used MediaRouter and MediaRouter2 simultaneously?
-    public void setControlCategories(@NonNull IMediaRouterClient client,
-            @Nullable List<String> categories) {
-        Objects.requireNonNull(client, "client must not be null");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
-                setControlCategoriesLocked(clientRecord, categories);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public void setControlCategories2(@NonNull IMediaRouter2Client client,
+    public void setControlCategories(@NonNull IMediaRouter2Client client,
             @Nullable List<String> categories) {
         Objects.requireNonNull(client, "client must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+                Client2Record clientRecord = mAllClientRecords.get(client.asBinder());
                 setControlCategoriesLocked(clientRecord, categories);
             }
         } finally {
@@ -295,37 +279,6 @@
         }
     }
 
-
-    public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) {
-        Objects.requireNonNull(client, "client must not be null");
-
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
-        final int userId = UserHandle.getUserId(uid);
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                registerClient1Locked(client, packageName, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public void unregisterClient(@NonNull IMediaRouterClient client) {
-        Objects.requireNonNull(client, "client must not be null");
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                unregisterClient1Locked(client);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     //TODO: Review this is handling multi-user properly.
     void switchUser() {
         synchronized (mLock) {
@@ -389,7 +342,7 @@
     }
 
     private void unregisterClient2Locked(IMediaRouter2Client client, boolean died) {
-        Client2Record clientRecord = (Client2Record) mAllClientRecords.remove(client.asBinder());
+        Client2Record clientRecord = mAllClientRecords.remove(client.asBinder());
         if (clientRecord != null) {
             UserRecord userRecord = clientRecord.mUserRecord;
             userRecord.mClientRecords.remove(clientRecord);
@@ -399,7 +352,7 @@
         }
     }
 
-    private void requestSelectRoute2Locked(ClientRecord clientRecord, boolean selectedByManager,
+    private void requestSelectRoute2Locked(Client2Record clientRecord, boolean selectedByManager,
             MediaRoute2Info route) {
         if (clientRecord != null) {
             MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
@@ -435,7 +388,7 @@
         }
     }
 
-    private void setControlCategoriesLocked(ClientRecord clientRecord, List<String> categories) {
+    private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
         if (clientRecord != null) {
             clientRecord.mControlCategories = categories;
 
@@ -448,7 +401,7 @@
     private void sendControlRequestLocked(IMediaRouter2Client client, MediaRoute2Info route,
             Intent request) {
         final IBinder binder = client.asBinder();
-        ClientRecord clientRecord = mAllClientRecords.get(binder);
+        Client2Record clientRecord = mAllClientRecords.get(binder);
 
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
@@ -460,7 +413,7 @@
     private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
             int volume) {
         final IBinder binder = client.asBinder();
-        ClientRecord clientRecord = mAllClientRecords.get(binder);
+        Client2Record clientRecord = mAllClientRecords.get(binder);
 
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
@@ -472,7 +425,7 @@
     private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
             int delta) {
         final IBinder binder = client.asBinder();
-        ClientRecord clientRecord = mAllClientRecords.get(binder);
+        Client2Record clientRecord = mAllClientRecords.get(binder);
 
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
@@ -511,7 +464,7 @@
                     obtainMessage(UserHandler::notifyRoutesToManager,
                             userRecord.mHandler, manager));
 
-            for (ClientRecord clientRecord : userRecord.mClientRecords) {
+            for (Client2Record clientRecord : userRecord.mClientRecords) {
                 // TODO: Do not use updateClientUsage since it updates all managers.
                 // Instead, Notify only to the manager that is currently being registered.
 
@@ -538,7 +491,7 @@
             String packageName, MediaRoute2Info route) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
         if (managerRecord != null) {
-            ClientRecord clientRecord =
+            Client2Record clientRecord =
                     managerRecord.mUserRecord.findClientRecordLocked(packageName);
             if (clientRecord == null) {
                 Slog.w(TAG, "Ignoring route selection for unknown client.");
@@ -600,41 +553,10 @@
         }
     }
 
-    private void registerClient1Locked(IMediaRouterClient client, String packageName,
-            int userId) {
-        final IBinder binder = client.asBinder();
-        if (mAllClientRecords.get(binder) == null) {
-            boolean newUser = false;
-            UserRecord userRecord = mUserRecords.get(userId);
-            if (userRecord == null) {
-                userRecord = new UserRecord(userId);
-                newUser = true;
-            }
-            ClientRecord clientRecord = new Client1Record(userRecord, client, packageName);
-
-            if (newUser) {
-                mUserRecords.put(userId, userRecord);
-                initializeUserLocked(userRecord);
-            }
-
-            userRecord.mClientRecords.add(clientRecord);
-            mAllClientRecords.put(binder, clientRecord);
-        }
-    }
-
-    private void unregisterClient1Locked(IMediaRouterClient client) {
-        ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
-        if (clientRecord != null) {
-            UserRecord userRecord = clientRecord.mUserRecord;
-            userRecord.mClientRecords.remove(clientRecord);
-            disposeUserIfNeededLocked(userRecord);
-        }
-    }
-
     final class UserRecord {
         public final int mUserId;
         //TODO: make records private for thread-safety
-        final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
+        final ArrayList<Client2Record> mClientRecords = new ArrayList<>();
         final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
         final UserHandler mHandler;
 
@@ -643,8 +565,8 @@
             mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this);
         }
 
-        ClientRecord findClientRecordLocked(String packageName) {
-            for (ClientRecord clientRecord : mClientRecords) {
+        Client2Record findClientRecordLocked(String packageName) {
+            for (Client2Record clientRecord : mClientRecords) {
                 if (TextUtils.equals(clientRecord.mPackageName, packageName)) {
                     return clientRecord;
                 }
@@ -653,44 +575,26 @@
         }
     }
 
-    class ClientRecord {
+    final class Client2Record implements IBinder.DeathRecipient {
         public final UserRecord mUserRecord;
         public final String mPackageName;
         public final List<Integer> mSelectRouteSequenceNumbers;
+        public final IMediaRouter2Client mClient;
+        public final int mUid;
+        public final int mPid;
+        public final boolean mTrusted;
 
         public List<String> mControlCategories;
         public boolean mIsManagerSelecting;
         public MediaRoute2Info mSelectingRoute;
         public MediaRoute2Info mSelectedRoute;
 
-        ClientRecord(UserRecord userRecord, String packageName) {
+        Client2Record(UserRecord userRecord, IMediaRouter2Client client,
+                int uid, int pid, String packageName, boolean trusted) {
             mUserRecord = userRecord;
             mPackageName = packageName;
             mSelectRouteSequenceNumbers = new ArrayList<>();
             mControlCategories = Collections.emptyList();
-        }
-    }
-
-    final class Client1Record extends ClientRecord {
-        public final IMediaRouterClient mClient;
-
-        Client1Record(UserRecord userRecord, IMediaRouterClient client,
-                String packageName) {
-            super(userRecord, packageName);
-            mClient = client;
-        }
-    }
-
-    final class Client2Record extends ClientRecord
-            implements IBinder.DeathRecipient {
-        public final IMediaRouter2Client mClient;
-        public final int mUid;
-        public final int mPid;
-        public final boolean mTrusted;
-
-        Client2Record(UserRecord userRecord, IMediaRouter2Client client,
-                int uid, int pid, String packageName, boolean trusted) {
-            super(userRecord, packageName);
             mClient = client;
             mUid = uid;
             mPid = pid;
@@ -909,15 +813,10 @@
                 return;
             }
 
-            ClientRecord clientRecord;
+            Client2Record clientRecord;
             synchronized (service.mLock) {
                 clientRecord = mUserRecord.findClientRecordLocked(clientPackageName);
             }
-            if (!(clientRecord instanceof Client2Record)) {
-                Log.w(TAG, "Ignoring route selection for unknown client.");
-                unselectRoute(clientPackageName, selectedRoute);
-                return;
-            }
 
             //TODO: handle a case such that controlHints is null. (How should we notify MR2?)
 
@@ -931,7 +830,7 @@
             clientRecord.mSelectingRoute = null;
             clientRecord.mSelectedRoute = selectedRoute;
 
-            notifyRouteSelectedToClient(((Client2Record) clientRecord).mClient,
+            notifyRouteSelectedToClient(clientRecord.mClient,
                     selectedRoute,
                     clientRecord.mIsManagerSelecting
                             ? MediaRouter2.SELECT_REASON_SYSTEM_SELECTED :
@@ -950,14 +849,10 @@
                 return;
             }
 
-            ClientRecord clientRecord;
+            Client2Record clientRecord;
             synchronized (service.mLock) {
                 clientRecord = mUserRecord.findClientRecordLocked(clientPackageName);
             }
-            if (!(clientRecord instanceof Client2Record)) {
-                Log.w(TAG, "Ignoring fallback route selection for unknown client.");
-                return;
-            }
 
             if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
                     clientRecord.mSelectingRoute.getUniqueId(), selectingRoute.getUniqueId())) {
@@ -972,7 +867,7 @@
             MediaRoute2Info fallbackRoute = null;
             clientRecord.mSelectedRoute = fallbackRoute;
 
-            notifyRouteSelectedToClient(((Client2Record) clientRecord).mClient,
+            notifyRouteSelectedToClient(clientRecord.mClient,
                     fallbackRoute,
                     MediaRouter2.SELECT_REASON_FALLBACK,
                     Bundle.EMPTY /* controlHints */);
@@ -1029,10 +924,8 @@
                 return clients;
             }
             synchronized (service.mLock) {
-                for (ClientRecord clientRecord : mUserRecord.mClientRecords) {
-                    if (clientRecord instanceof Client2Record) {
-                        clients.add(((Client2Record) clientRecord).mClient);
-                    }
+                for (Client2Record clientRecord : mUserRecord.mClientRecords) {
+                    clients.add(clientRecord.mClient);
                 }
             }
             return clients;
@@ -1157,7 +1050,7 @@
             }
         }
 
-        private void updateClientUsage(ClientRecord clientRecord) {
+        private void updateClientUsage(Client2Record clientRecord) {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
                 return;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 9336af4..9c99e8f 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -249,7 +249,6 @@
             synchronized (mLock) {
                 registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted);
             }
-            mService2.registerClient(client, packageName);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -290,7 +289,6 @@
             synchronized (mLock) {
                 unregisterClientLocked(client, false);
             }
-            mService2.unregisterClient(client);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -397,12 +395,6 @@
 
     // Binder call
     @Override
-    public void setControlCategories(IMediaRouterClient client, List<String> controlCategories) {
-        mService2.setControlCategories(client, controlCategories);
-    }
-
-    // Binder call
-    @Override
     public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
         if (client == null) {
             throw new IllegalArgumentException("client must not be null");
@@ -501,8 +493,8 @@
 
     // Binder call
     @Override
-    public void setControlCategories2(IMediaRouter2Client client, List<String> categories) {
-        mService2.setControlCategories2(client, categories);
+    public void setControlCategories(IMediaRouter2Client client, List<String> categories) {
+        mService2.setControlCategories(client, categories);
     }
 
     // Binder call
@@ -601,7 +593,6 @@
         synchronized (mLock) {
             unregisterClientLocked(clientRecord.mClient, true);
         }
-        mService2.unregisterClient(clientRecord.mClient);
     }
 
     private void registerClientLocked(IMediaRouterClient client,
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 77fbe41..4cb41da 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -16,7 +16,7 @@
 
 package com.android.server.net;
 
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.provider.Settings.ACTION_VPN_SETTINGS;
 
 import android.annotation.NonNull;
@@ -202,8 +202,7 @@
         mVpn.setLockdown(true);
 
         final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
-        mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, mHandler);
-
+        mContext.registerReceiver(mResetReceiver, resetFilter, NETWORK_STACK, mHandler);
         handleStateChangedLocked();
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 32d4b72..dfdc2c1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -20,6 +20,9 @@
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
@@ -33,6 +36,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
@@ -153,6 +157,7 @@
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
@@ -825,7 +830,7 @@
 
             // watch for network interfaces to be claimed
             final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
-            mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
+            mContext.registerReceiver(mConnReceiver, connFilter, NETWORK_STACK, mHandler);
 
             // listen for package changes to update policy
             final IntentFilter packageFilter = new IntentFilter();
@@ -1128,7 +1133,7 @@
         @Override
         public void limitReached(String limitName, String iface) {
             // only someone like NMS should be calling us
-            mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+            NetworkStack.checkNetworkStackPermission(mContext);
 
             if (!LIMIT_GLOBAL_ALERT.equals(limitName)) {
                 mHandler.obtainMessage(MSG_LIMIT_REACHED, iface).sendToTarget();
@@ -1483,7 +1488,7 @@
     private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+            // on background handler thread, and verified NETWORK_STACK
             // permission above.
             updateNetworksInternal();
         }
@@ -2721,17 +2726,35 @@
         return changed;
     }
 
+    private boolean checkAnyPermissionOf(String... permissions) {
+        for (String permission : permissions) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void enforceAnyPermissionOf(String... permissions) {
+        if (!checkAnyPermissionOf(permissions)) {
+            throw new SecurityException("Requires one of the following permissions: "
+                    + String.join(", ", permissions) + ".");
+        }
+    }
+
     @Override
     public void registerListener(INetworkPolicyListener listener) {
-        // TODO: create permission for observing network policy
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
+        //  have declared OBSERVE_NETWORK_POLICY.
+        enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
         mListeners.register(listener);
     }
 
     @Override
     public void unregisterListener(INetworkPolicyListener listener) {
-        // TODO: create permission for observing network policy
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
+        //  have declared OBSERVE_NETWORK_POLICY.
+        enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
         mListeners.unregister(listener);
     }
 
@@ -4965,7 +4988,7 @@
 
     @Override
     public void factoryReset(String subscriber) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
 
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
@@ -4998,7 +5021,7 @@
     public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
         final long startTime = mStatLogger.getTime();
 
-        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
         final boolean isBackgroundRestricted;
         synchronized (mUidRulesFirstLock) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e473c96..16424f2 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -17,7 +17,6 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
@@ -91,6 +90,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
+import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStats.NonMonotonicObserver;
@@ -1020,8 +1020,6 @@
     private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // on background handler thread, and verified CONNECTIVITY_INTERNAL
-            // permission above.
             performPoll(FLAG_PERSIST_NETWORK);
         }
     };
@@ -1095,7 +1093,7 @@
         @Override
         public void limitReached(String limitName, String iface) {
             // only someone like NMS should be calling us
-            mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+            NetworkStack.checkNetworkStackPermission(mContext);
 
             if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
                 // kick off background poll to collect network stats unless there is already
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 863991c..293fd40 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -324,6 +324,7 @@
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     static final int INVALID_UID = -1;
+    static final String ROOT_PKG = "root";
 
     static final boolean ENABLE_BLOCKED_TOASTS = true;
 
@@ -7779,7 +7780,8 @@
 
     protected boolean isUidSystemOrPhone(int uid) {
         final int appid = UserHandle.getAppId(uid);
-        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
+        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID
+                || uid == Process.ROOT_UID);
     }
 
     // TODO: Most calls should probably move to isCallerSystem.
@@ -7788,7 +7790,8 @@
     }
 
     private void checkCallerIsSystemOrShell() {
-        if (Binder.getCallingUid() == Process.SHELL_UID) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             return;
         }
         checkCallerIsSystem();
@@ -7802,7 +7805,8 @@
     }
 
     private void checkCallerIsSystemOrSystemUiOrShell() {
-        if (Binder.getCallingUid() == Process.SHELL_UID) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             return;
         }
         if (isCallerSystemOrPhone()) {
@@ -7877,6 +7881,9 @@
     }
 
     private void checkCallerIsSameApp(String pkg, int uid, int userId) {
+        if (uid == Process.ROOT_UID && ROOT_PKG.equals(pkg)) {
+            return;
+        }
         try {
             ApplicationInfo ai = mPackageManager.getApplicationInfo(
                     pkg, 0, userId);
@@ -8268,7 +8275,7 @@
             try {
                 assistant.onAllowedAdjustmentsChanged();
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify assistant (capabilities): " + assistant, ex);
+                Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
             }
         }
 
@@ -8278,7 +8285,7 @@
             try {
                 assistant.onNotificationsSeen(keys);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify assistant (seen): " + assistant, ex);
+                Slog.e(TAG, "unable to notify assistant (seen): " + info, ex);
             }
         }
 
@@ -8584,7 +8591,7 @@
                         listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
                     } catch (RemoteException ex) {
                         Slog.e(TAG, "unable to notify listener "
-                                + "(hideSilentStatusIcons): " + listener, ex);
+                                + "(hideSilentStatusIcons): " + info, ex);
                     }
                 });
             }
@@ -8877,7 +8884,7 @@
             try {
                 listener.onNotificationPosted(sbnHolder, rankingUpdate);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (posted): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
             }
         }
 
@@ -8891,7 +8898,7 @@
             try {
                 listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (removed): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
             }
         }
 
@@ -8901,7 +8908,7 @@
             try {
                 listener.onNotificationRankingUpdate(rankingUpdate);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (ranking update): " + info, ex);
             }
         }
 
@@ -8910,7 +8917,7 @@
             try {
                 listener.onListenerHintsChanged(hints);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (listener hints): " + info, ex);
             }
         }
 
@@ -8920,7 +8927,7 @@
             try {
                 listener.onInterruptionFilterChanged(interruptionFilter);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (interruption filter): " + info, ex);
             }
         }
 
@@ -8931,7 +8938,7 @@
             try {
                 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (channel changed): " + info, ex);
             }
         }
 
@@ -8942,7 +8949,7 @@
             try {
                 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
+                Slog.e(TAG, "unable to notify listener (channel group changed): " + info, ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 50f16bc..2b5ba25 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -125,9 +125,13 @@
         final int callingUid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
-            String[] packages = mPm.getPackagesForUid(callingUid);
-            if (packages != null && packages.length > 0) {
-                callingPackage = packages[0];
+            if (callingUid == Process.ROOT_UID) {
+                callingPackage = NotificationManagerService.ROOT_PKG;
+            } else {
+                String[] packages = mPm.getPackagesForUid(callingUid);
+                if (packages != null && packages.length > 0) {
+                    callingPackage = packages[0];
+                }
             }
         } catch (Exception e) {
             Slog.e(TAG, "failed to get caller pkg", e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d63e704..4852947 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -133,6 +133,7 @@
     private static final int MSG_COMMIT = 1;
     private static final int MSG_ON_PACKAGE_INSTALLED = 2;
     private static final int MSG_SEAL = 3;
+    private static final int MSG_TRANSFER = 4;
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
@@ -332,6 +333,9 @@
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
+            SomeArgs args;
+            String packageName;
+            IntentSender statusReceiver;
             switch (msg.what) {
                 case MSG_SEAL:
                     handleSeal((IntentSender) msg.obj);
@@ -339,12 +343,20 @@
                 case MSG_COMMIT:
                     handleCommit();
                     break;
+                case MSG_TRANSFER:
+                    args = (SomeArgs) msg.obj;
+                    packageName = (String) args.arg1;
+                    statusReceiver = (IntentSender) args.arg2;
+                    args.recycle();
+
+                    handleTransfer(statusReceiver, packageName);
+                    break;
                 case MSG_ON_PACKAGE_INSTALLED:
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    final String packageName = (String) args.arg1;
+                    args = (SomeArgs) msg.obj;
+                    packageName = (String) args.arg1;
                     final String message = (String) args.arg2;
                     final Bundle extras = (Bundle) args.arg3;
-                    final IntentSender statusReceiver = (IntentSender) args.arg4;
+                    statusReceiver = (IntentSender) args.arg4;
                     final int returnCode = args.argi1;
                     args.recycle();
 
@@ -378,7 +390,7 @@
      * Checks if the permissions still need to be confirmed.
      *
      * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
-     * installer might still {@link #transfer(String) change}.
+     * installer might still {@link #transfer(String, IntentSender) change}.
      *
      * @return {@code true} iff we need to ask to confirm the permissions?
      */
@@ -1183,13 +1195,11 @@
         }
     }
 
-    @Override
-    public void transfer(String packageName) {
-        Preconditions.checkNotNull(packageName);
-
+    private int assertCanBeTransferredAndReturnNewOwner(String packageName)
+            throws PackageManager.NameNotFoundException {
         ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
         if (newOwnerAppInfo == null) {
-            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+            throw new PackageManager.NameNotFoundException(packageName);
         }
 
         if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
@@ -1204,31 +1214,104 @@
             throw new SecurityException("Can only transfer sessions that use public options");
         }
 
-        List<PackageInstallerSession> childSessions = getChildSessions();
+        return newOwnerAppInfo.uid;
+    }
+
+    @Override
+    public void transfer(String packageName, IntentSender statusReceiver) {
+        Preconditions.checkNotNull(statusReceiver);
+        Preconditions.checkNotNull(packageName);
+
+        try {
+            assertCanBeTransferredAndReturnNewOwner(packageName);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new ParcelableException(e);
+        }
 
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("transfer");
+        }
 
-            try {
-                sealAndValidateLocked(childSessions);
-            } catch (PackageManagerException e) {
-                throw new IllegalArgumentException("Package is not valid", e);
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = packageName;
+        args.arg2 = statusReceiver;
+
+        mHandler.obtainMessage(MSG_TRANSFER, args).sendToTarget();
+    }
+
+    private void handleTransfer(IntentSender statusReceiver, String packageName) {
+        List<PackageInstallerSession> childSessions = getChildSessions();
+
+        try {
+            final int uid = assertCanBeTransferredAndReturnNewOwner(packageName);
+
+            synchronized (mLock) {
+                assertPreparedAndNotSealedLocked("transfer");
+
+                try {
+                    sealAndValidateLocked(childSessions);
+                } catch (PackageManagerException e) {
+                    throw new IllegalArgumentException("Package is not valid", e);
+                }
+
+                if (!mPackageName.equals(mInstallSource.installerPackageName)) {
+                    throw new SecurityException(
+                            "Can only transfer sessions that update the original installer");
+                }
+
+                mInstallerUid = uid;
+                mInstallSource = InstallSource.create(packageName, null, packageName, false);
             }
-
-            if (!mPackageName.equals(mInstallSource.installerPackageName)) {
-                throw new SecurityException("Can only transfer sessions that update the original "
-                        + "installer");
-            }
-
-            mInstallerUid = newOwnerAppInfo.uid;
-            mInstallSource = InstallSource.create(packageName, null, packageName, false);
+        } catch (PackageManager.NameNotFoundException e) {
+            onSessionTransferStatus(statusReceiver, packageName,
+                    PackageInstaller.STATUS_FAILURE_NAME_NOT_FOUND, e.getMessage());
+            return;
+        } catch (IllegalStateException e) {
+            onSessionTransferStatus(statusReceiver, packageName,
+                    PackageInstaller.STATUS_FAILURE_ILLEGAL_STATE, e.getMessage());
+            return;
+        } catch (SecurityException e) {
+            onSessionTransferStatus(statusReceiver, packageName,
+                    PackageInstaller.STATUS_FAILURE_SECURITY, e.getMessage());
+            return;
+        } catch (Throwable e) {
+            onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_FAILURE,
+                    e.getMessage());
+            return;
         }
 
         // Persist the fact that we've sealed ourselves to prevent
         // mutations of any hard links we create. We do this without holding
         // the session lock, since otherwise it's a lock inversion.
         mCallback.onSessionSealedBlocking(this);
+
+        // Report success.
+        onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_SUCCESS, null);
+    }
+
+    private void onSessionTransferStatus(IntentSender statusReceiver, String otherPackageName,
+            int status, String statusMessage) {
+        final String packageName;
+        synchronized (mLock) {
+            packageName = mPackageName;
+        }
+
+        final Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
+        fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+        fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, otherPackageName);
+
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS, status);
+        if (!TextUtils.isEmpty(statusMessage)) {
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
+        }
+
+        try {
+            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException ignored) {
+        }
+
     }
 
     private void handleCommit() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8439a0d..4e0e4ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2364,6 +2364,37 @@
         PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
         t.traceEnd(); // "create package manager"
 
+        injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+                packageName -> {
+                    synchronized (m.mInstallLock) {
+                        final PackageParser.Package pkg;
+                        final SharedUserSetting sharedUser;
+                        synchronized (m.mLock) {
+                            PackageSetting ps = m.mSettings.getPackageLPr(packageName);
+                            if (ps == null) {
+                                Slog.e(TAG, "Failed to find package setting " + packageName);
+                                return;
+                            }
+                            pkg = ps.pkg;
+                            sharedUser = ps.sharedUser;
+                        }
+
+                        if (pkg == null) {
+                            Slog.e(TAG, "Failed to find package " + packageName);
+                            return;
+                        }
+                        final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+                                m.mInjector.getCompatibility());
+
+                        if (!newSeInfo.equals(pkg.applicationInfo.seInfo)) {
+                            Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+                                    + pkg.applicationInfo.seInfo + " to: " + newSeInfo);
+                            pkg.applicationInfo.seInfo = newSeInfo;
+                            m.prepareAppDataAfterInstallLIF(pkg);
+                        }
+                    }
+                });
+
         m.installWhitelistedSystemPackages();
         ServiceManager.addService("package", m);
         final PackageManagerNative pmn = m.new PackageManagerNative();
@@ -10784,24 +10815,8 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
         }
 
-        // Apps which share a sharedUserId must be placed in the same selinux domain. If this
-        // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
-        // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
-        // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
-        // least restrictive selinux domain.
-        // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
-        // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
-        // ensures that all packages continue to run in the same selinux domain.
-        final int targetSdkVersion =
-            ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
-            sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
-        // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
-        // They currently can be if the sharedUser apps are signed with the platform key.
-        final boolean isPrivileged = (sharedUserSetting != null) ?
-            sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
-
-        pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
-                targetSdkVersion);
+        pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, sharedUserSetting,
+                injector.getCompatibility());
         pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
                 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 9afc9a8..1b73602 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3279,7 +3279,7 @@
         pw.println("       [--user USER_ID] INTENT");
         pw.println("    Prints all broadcast receivers that can handle the given INTENT.");
         pw.println("");
-        pw.println("  install [-rtsfdgw] [-i PACKAGE] [--user USER_ID|all|current]");
+        pw.println("  install [-rtfdgw] [-i PACKAGE] [--user USER_ID|all|current]");
         pw.println("       [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
         pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
         pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
@@ -3293,7 +3293,6 @@
         pw.println("      -R: disallow replacement of existing application");
         pw.println("      -t: allow test packages");
         pw.println("      -i: specify package name of installer owning the app");
-        pw.println("      -s: install application on sdcard");
         pw.println("      -f: install application on internal flash");
         pw.println("      -d: allow version code downgrade (debuggable packages only)");
         pw.println("      -p: partial application install (new split on top of existing pkg)");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b464988..d20f20f 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
@@ -23,6 +25,8 @@
 import android.util.Slog;
 import android.util.Xml;
 
+import com.android.server.compat.PlatformCompat;
+
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -72,6 +76,19 @@
     // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
     private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
 
+    /**
+     * This change gates apps access to untrusted_app_R-targetSDk SELinux domain. Allows opt-in
+     * to R targetSdkVersion enforced changes without changing target SDK. Turning this change
+     * off for an app targeting R is a no-op.
+     *
+     * <p>Has no effect for apps using shared user id.
+     *
+     * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
+     */
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
+    @ChangeId
+    static final long SELINUX_LATEST_CHANGES = 143539591L;
+
     // Only initialize sMacPermissions once.
     static {
         // Platform mac permissions.
@@ -319,6 +336,48 @@
         }
     }
 
+    private static int getTargetSdkVersionForSeInfo(PackageParser.Package pkg,
+            SharedUserSetting sharedUserSetting, PlatformCompat compatibility) {
+        // Apps which share a sharedUserId must be placed in the same selinux domain. If this
+        // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
+        // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
+        // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
+        // least restrictive selinux domain.
+        // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
+        // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
+        // ensures that all packages continue to run in the same selinux domain.
+        if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
+            return sharedUserSetting.seInfoTargetSdkVersion;
+        }
+        if (compatibility.isChangeEnabled(SELINUX_LATEST_CHANGES, pkg.applicationInfo)) {
+            return android.os.Build.VERSION_CODES.R;
+        }
+
+        return pkg.applicationInfo.targetSdkVersion;
+    }
+
+    /**
+     * Selects a security label to a package based on input parameters and the seinfo tag taken
+     * from a matched policy. All signature based policy stanzas are consulted and, if no match
+     * is found, the default seinfo label of 'default' is used. The security label is attached to
+     * the ApplicationInfo instance of the package.
+     *
+     * @param pkg               object representing the package to be labeled.
+     * @param sharedUserSetting if the app shares a sharedUserId, then this has the shared setting.
+     * @param compatibility     the PlatformCompat service to ask about state of compat changes.
+     * @return String representing the resulting seinfo.
+     */
+    public static String getSeInfo(PackageParser.Package pkg, SharedUserSetting sharedUserSetting,
+            PlatformCompat compatibility) {
+        final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUserSetting,
+                compatibility);
+        // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
+        // They currently can be if the sharedUser apps are signed with the platform key.
+        final boolean isPrivileged = (sharedUserSetting != null)
+                ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+        return getSeInfo(pkg, isPrivileged, targetSdkVersion);
+    }
+
     /**
      * Selects a security label to a package based on input parameters and the seinfo tag taken
      * from a matched policy. All signature based policy stanzas are consulted and, if no match
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index faff394..14cbe75 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3107,36 +3107,11 @@
             }
         }
 
-        // First try to use a pre-created user (if available).
-        // TODO(b/142482943): Move this to its own function later.
-        if (!preCreate
-                && (parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails))) {
-            final UserData preCreatedUserData;
-            synchronized (mUsersLock) {
-                preCreatedUserData = getPreCreatedUserLU(userType);
-            }
-            if (preCreatedUserData != null) {
-                final UserInfo preCreatedUser = preCreatedUserData.info;
-                final int newFlags = preCreatedUser.flags | flags;
-                if (!checkUserTypeConsistency(newFlags)) {
-                    Slog.wtf(LOG_TAG, "Cannot reuse pre-created user " + preCreatedUser.id
-                            + " of type " + userType + " because flags are inconsistent. "
-                            + "Flags (" + Integer.toHexString(flags) + "); preCreatedUserFlags ( "
-                            + Integer.toHexString(preCreatedUser.flags) + ").");
-                } else {
-                    Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " of type "
-                            + userType + " and bestowing on it flags "
-                            + UserInfo.flagsToString(flags));
-                    preCreatedUser.name = name;
-                    preCreatedUser.flags = newFlags;
-                    preCreatedUser.preCreated = false;
-                    preCreatedUser.creationTime = getCreationTime();
-
-                    dispatchUserAddedIntent(preCreatedUser);
-                    writeUserLP(preCreatedUserData);
-                    writeUserListLP();
-                    return preCreatedUser;
-                }
+        // Try to use a pre-created user (if available).
+        if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) {
+            final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name);
+            if (preCreatedUser != null) {
+                return preCreatedUser;
             }
         }
 
@@ -3210,6 +3185,13 @@
                         flags |= UserInfo.FLAG_EPHEMERAL;
                     }
 
+                    // Always clear EPHEMERAL for pre-created users, otherwise the storage key
+                    // won't be persisted. The flag will be re-added (if needed) when the
+                    // pre-created user is "converted" to a normal user.
+                    if (preCreate) {
+                        flags &= ~UserInfo.FLAG_EPHEMERAL;
+                    }
+
                     userInfo = new UserInfo(userId, name, null, flags, userType);
                     userInfo.serialNumber = mNextSerialNumber++;
                     userInfo.creationTime = getCreationTime();
@@ -3313,6 +3295,44 @@
         return userInfo;
     }
 
+    /**
+     * Finds and converts a previously pre-created user into a regular user, if possible.
+     *
+     * @return the converted user, or {@code null} if no pre-created user could be converted.
+     */
+    private @Nullable UserInfo convertPreCreatedUserIfPossible(String userType,
+            @UserInfoFlag int flags, String name) {
+        final UserData preCreatedUserData;
+        synchronized (mUsersLock) {
+            preCreatedUserData = getPreCreatedUserLU(userType);
+        }
+        if (preCreatedUserData == null) {
+            return null;
+        }
+        final UserInfo preCreatedUser = preCreatedUserData.info;
+        final int newFlags = preCreatedUser.flags | flags;
+        if (!checkUserTypeConsistency(newFlags)) {
+            Slog.wtf(LOG_TAG, "Cannot reuse pre-created user " + preCreatedUser.id
+                    + " of type " + userType + " because flags are inconsistent. "
+                    + "Flags (" + Integer.toHexString(flags) + "); preCreatedUserFlags ( "
+                    + Integer.toHexString(preCreatedUser.flags) + ").");
+            return null;
+        }
+        Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " of type "
+                + userType + " and bestowing on it flags " + UserInfo.flagsToString(flags));
+        preCreatedUser.name = name;
+        preCreatedUser.flags = newFlags;
+        preCreatedUser.preCreated = false;
+        preCreatedUser.creationTime = getCreationTime();
+
+        dispatchUserAddedIntent(preCreatedUser);
+        synchronized (mPackagesLock) {
+            writeUserLP(preCreatedUserData);
+            writeUserListLP();
+        }
+        return preCreatedUser;
+    }
+
     /** Checks that the flags do not contain mutually exclusive types/properties. */
     static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
         // Mask to check that flags don't refer to multiple user types.
@@ -4428,6 +4448,7 @@
         pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
         pw.println("  All guests ephemeral: " + Resources.getSystem().getBoolean(
                 com.android.internal.R.bool.config_guestUserEphemeral));
+        pw.println("  Force ephemeral users: " + mForceEphemeralUsers);
         pw.println("  Is split-system user: " + UserManager.isSplitSystemUser());
         pw.println("  Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
         pw.println("  User version: " + mUserVersion);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f071c65..9b50307 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -578,10 +578,6 @@
                     if (newValue) {
                         android.provider.Settings.Global.putStringForUser(
                                 context.getContentResolver(),
-                                android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
-                                userId);
-                        android.provider.Settings.Global.putStringForUser(
-                                context.getContentResolver(),
                                 android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
                                 userId);
                     }
@@ -717,7 +713,6 @@
                 restriction = UserManager.DISALLOW_DEBUGGING_FEATURES;
                 break;
 
-            case android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE:
             case android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB:
                 if ("1".equals(value)) {
                     return false;
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 6abfbdc..0a6a435 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -85,28 +85,30 @@
      * System Property whether to only install system packages on a user if they're whitelisted for
      * that user type. These are flags and can be freely combined.
      * <ul>
-     * <li> 0 (0b000) - disable whitelist (install all system packages; no logging)</li>
-     * <li> 1 (0b001) - enforce (only install system packages if they are whitelisted)</li>
-     * <li> 2 (0b010) - log (log when a non-whitelisted package is run)</li>
-     * <li> 4 (0b100) - implicitly whitelist any package not mentioned in the whitelist</li>
-     * <li>-1         - use device default (as defined in res/res/values/config.xml)</li>
+     * <li> 0 (0b0000) - disable whitelist (install all system packages; no logging)</li>
+     * <li> 1 (0b0001) - enforce (only install system packages if they are whitelisted)</li>
+     * <li> 2 (0b0010) - log (log when a non-whitelisted package is run)</li>
+     * <li> 4 (0b0100) - implicitly whitelist any package not mentioned in the whitelist</li>
+     * <li> 8 (0b1000) - ignore OTAs (don't install system packages during OTAs)</li>
+     * <li>-1          - use device default (as defined in res/res/values/config.xml)</li>
      * </ul>
      * Note: This list must be kept current with config_userTypePackageWhitelistMode in
      * frameworks/base/core/res/res/values/config.xml
      */
     static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
     static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
-    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
-    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b010;
-    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
+    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b0001;
+    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b0010;
+    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b0100;
+    static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0b1000;
     static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
 
     @IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
             USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
             USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
-            USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
             USER_TYPE_PACKAGE_WHITELIST_MODE_LOG,
             USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST,
+            USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PackageWhitelistMode {}
@@ -169,11 +171,17 @@
     boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
         final int mode = getWhitelistMode();
         checkWhitelistedSystemPackages(mode);
-        if (!isUpgrade && !isFirstBoot) {
+        final boolean isConsideredUpgrade = isUpgrade && !isIgnoreOtaMode(mode);
+        if (!isConsideredUpgrade && !isFirstBoot) {
+            return false;
+        }
+        if (isFirstBoot && !isEnforceMode(mode)) {
+            // Note that if !isEnforceMode, we nonetheless still install packages if isUpgrade
+            // in order to undo any previous non-installing. isFirstBoot lacks this requirement.
             return false;
         }
         Slog.i(TAG, "Reviewing whitelisted packages due to "
-                + (isFirstBoot ? "[firstBoot]" : "") + (isUpgrade ? "[upgrade]" : ""));
+                + (isFirstBoot ? "[firstBoot]" : "") + (isConsideredUpgrade ? "[upgrade]" : ""));
         final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
         // Install/uninstall system packages per user.
         for (int userId : mUm.getUserIds()) {
@@ -185,7 +193,7 @@
                 final boolean install =
                         (userWhitelist == null || userWhitelist.contains(pkg.packageName))
                         && !pkg.applicationInfo.hiddenUntilInstalled;
-                if (isUpgrade && !isFirstBoot && !install) {
+                if (isConsideredUpgrade && !isFirstBoot && !install) {
                     return; // To be careful, we don’t uninstall apps during OTAs
                 }
                 final boolean changed = pmInt.setInstalled(pkg, userId, install);
@@ -245,6 +253,19 @@
     }
 
     /**
+     * Whether to ignore OTAs, and therefore not install missing system packages during OTAs.
+     * <p>Note:
+     * If in this mode, old system packages will not be installed on pre-existing users during OTAs.
+     * Any system packages that had not been installed at the time of the user's creation,
+     * due to {@link UserSystemPackageInstaller}'s previous actions, will therefore continue to
+     * remain uninstalled, even if the whitelist (or enforcement mode) now declares that they should
+     * be.
+     */
+    boolean isIgnoreOtaMode() {
+        return isIgnoreOtaMode(getWhitelistMode());
+    }
+
+    /**
      * Whether to log a warning concerning potential problems with the user-type package whitelist.
      */
     boolean isLogMode() {
@@ -264,6 +285,11 @@
         return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE) != 0;
     }
 
+    /** See {@link #isIgnoreOtaMode()}. */
+    private static boolean isIgnoreOtaMode(int whitelistMode) {
+        return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA) != 0;
+    }
+
     /** See {@link #isLogMode()}. */
     private static boolean isLogMode(int whitelistMode) {
         return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_LOG) != 0;
@@ -536,6 +562,7 @@
         pw.print(isEnforceMode(mode) ? " (enforced)" : "");
         pw.print(isLogMode(mode) ? " (logged)" : "");
         pw.print(isImplicitWhitelistMode(mode) ? " (implicit)" : "");
+        pw.print(isIgnoreOtaMode(mode) ? " (ignore OTAs)" : "");
         pw.println();
 
         pw.print(prefix); pw.println("Legend");
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index ed7d234..eec0d5f 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
 import static android.provider.Settings.System.ADAPTIVE_SLEEP;
 
 import android.Manifest;
@@ -33,6 +34,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.attention.AttentionService;
 import android.util.Slog;
@@ -58,6 +60,22 @@
     private static final String TAG = "AttentionDetector";
     private static final boolean DEBUG = false;
 
+    /**
+     * DeviceConfig flag name, describes how much in advance to start checking attention before the
+     * dim event.
+     */
+    static final String KEY_PRE_DIM_CHECK_DURATION_MILLIS = "pre_dim_check_duration_millis";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    static final long DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS = 2_000;
+
+    /** DeviceConfig flag name, describes how long to run the check beyond the screen dim event. */
+    static final String KEY_POST_DIM_CHECK_DURATION_MILLIS =
+            "post_dim_check_duration_millis";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    static final long DEFAULT_POST_DIM_CHECK_DURATION_MILLIS = 0;
+
     private Context mContext;
 
     private boolean mIsSettingEnabled;
@@ -90,11 +108,6 @@
     protected int mRequestId;
 
     /**
-     * {@link android.service.attention.AttentionService} API timeout.
-     */
-    private long mMaxAttentionApiTimeoutMillis;
-
-    /**
      * Last known user activity.
      */
     private long mLastUserActivityTime;
@@ -124,6 +137,9 @@
     @VisibleForTesting
     AttentionCallbackInternalImpl mCallback;
 
+    /** Keep the last used post dim timeout for the dumpsys. */
+    private long mLastPostDimTimeout;
+
     public AttentionDetector(Runnable onUserAttention, Object lock) {
         mOnUserAttention = onUserAttention;
         mLock = lock;
@@ -149,8 +165,6 @@
         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
         mMaximumExtensionMillis = context.getResources().getInteger(
                 com.android.internal.R.integer.config_attentionMaximumExtension);
-        mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
-                com.android.internal.R.integer.config_attentionApiTimeout);
 
         try {
             final UserSwitchObserver observer = new UserSwitchObserver();
@@ -169,7 +183,8 @@
                 }, UserHandle.USER_ALL);
     }
 
-    public long updateUserActivity(long nextScreenDimming) {
+    /** To be called in {@link PowerManagerService#updateUserActivitySummaryLocked}. */
+    public long updateUserActivity(long nextScreenDimming, long dimDurationMillis) {
         if (nextScreenDimming == mLastActedOnNextScreenDimming
                 || !mIsSettingEnabled
                 || mWindowManager.isKeyguardShowingAndNotOccluded()) {
@@ -184,7 +199,7 @@
         }
 
         final long now = SystemClock.uptimeMillis();
-        final long whenToCheck = nextScreenDimming - getAttentionTimeout();
+        final long whenToCheck = nextScreenDimming - getPreDimCheckDurationMillis();
         final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
         if (now < whenToCheck) {
             if (DEBUG) {
@@ -213,7 +228,9 @@
         mLastActedOnNextScreenDimming = nextScreenDimming;
         mCallback = new AttentionCallbackInternalImpl(mRequestId);
         Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
-        final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
+        final boolean sent = mAttentionManager.checkAttention(
+                getPreDimCheckDurationMillis() + getPostDimCheckDurationMillis(dimDurationMillis),
+                mCallback);
         if (!sent) {
             mRequested.set(false);
         }
@@ -272,11 +289,6 @@
         }
     }
 
-    @VisibleForTesting
-    long getAttentionTimeout() {
-        return mMaxAttentionApiTimeoutMillis;
-    }
-
     /**
      * {@see AttentionManagerInternal#isAttentionServiceSupported}
      */
@@ -301,12 +313,44 @@
         pw.println("AttentionDetector:");
         pw.println(" mIsSettingEnabled=" + mIsSettingEnabled);
         pw.println(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
-        pw.println(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
+        pw.println(" preDimCheckDurationMillis=" + getPreDimCheckDurationMillis());
+        pw.println(" postDimCheckDurationMillis=" + mLastPostDimTimeout);
         pw.println(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
         pw.println(" mAttentionServiceSupported=" + isAttentionServiceSupported());
         pw.println(" mRequested=" + mRequested);
     }
 
+    /** How long to check <b>before</b> the screen dims, capped at the dim duration. */
+    @VisibleForTesting
+    protected long getPreDimCheckDurationMillis() {
+        final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS,
+                DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
+
+        if (millis < 0 || millis > 13_000) {
+            Slog.w(TAG, "Bad flag value supplied for: " + KEY_PRE_DIM_CHECK_DURATION_MILLIS);
+            return DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS;
+        }
+
+        return millis;
+    }
+
+    /** How long to check <b>after</b> the screen dims, capped at the dim duration. */
+    @VisibleForTesting
+    protected long getPostDimCheckDurationMillis(long dimDurationMillis) {
+        final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS,
+                DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
+
+        if (millis < 0 || millis > 10_000) {
+            Slog.w(TAG, "Bad flag value supplied for: " + KEY_POST_DIM_CHECK_DURATION_MILLIS);
+            return DEFAULT_POST_DIM_CHECK_DURATION_MILLIS;
+        }
+
+        mLastPostDimTimeout = Math.min(millis, dimDurationMillis);
+        return mLastPostDimTimeout;
+    }
+
     @VisibleForTesting
     final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
         private final int mId;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9cfd095..114a16a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2140,9 +2140,11 @@
                     nextTimeout = -1;
                 }
 
-                if ((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                if (((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                        || (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0)
                         && (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) == 0) {
-                    nextTimeout = mAttentionDetector.updateUserActivity(nextTimeout);
+                    nextTimeout = mAttentionDetector.updateUserActivity(nextTimeout,
+                            screenDimDuration);
                 }
 
                 if (nextProfileTimeout > 0) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 01f29dc..00779ec 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -67,6 +67,7 @@
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -82,6 +83,7 @@
 class RollbackManagerServiceImpl extends IRollbackManager.Stub {
 
     private static final String TAG = "RollbackManager";
+    private static final boolean LOCAL_LOGV = false;
 
     // Rollbacks expire after 14 days.
     private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
@@ -220,6 +222,9 @@
                 if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
                     int token = intent.getIntExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+                    if (LOCAL_LOGV) {
+                        Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
+                    }
                     synchronized (mLock) {
                         for (NewRollback rollback : mNewRollbacks) {
                             if (rollback.hasToken(token)) {
@@ -269,10 +274,17 @@
                 String action = intent.getAction();
                 if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                     String packageName = intent.getData().getSchemeSpecificPart();
+                    if (LOCAL_LOGV) {
+                        Slog.v(TAG, "broadcast=ACTION_PACKAGE_REPLACED" + " pkg=" + packageName);
+                    }
                     onPackageReplaced(packageName);
                 }
                 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
                     String packageName = intent.getData().getSchemeSpecificPart();
+                    if (LOCAL_LOGV) {
+                        Slog.v(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED"
+                                + " pkg=" + packageName);
+                    }
                     onPackageFullyRemoved(packageName);
                 }
             }
@@ -359,7 +371,7 @@
      */
     private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
             String callerPackageName, IntentSender statusReceiver) {
-        Slog.i(TAG, "Initiating rollback");
+        Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
 
         Rollback rollback = getRollbackForId(rollbackId);
         if (rollback == null) {
@@ -444,6 +456,9 @@
     }
 
     void onUnlockUser(int userId) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "onUnlockUser id=" + userId);
+        }
         // In order to ensure that no package begins running while a backup or restore is taking
         // place, onUnlockUser must remain blocked until all pending backups and restores have
         // completed.
@@ -611,6 +626,9 @@
                 if (!now.isBefore(
                         rollbackTimestamp
                                 .plusMillis(mRollbackLifetimeDurationInMillis))) {
+                    if (LOCAL_LOGV) {
+                        Slog.v(TAG, "runExpiration id=" + rollback.info.getRollbackId());
+                    }
                     iter.remove();
                     rollback.delete(mAppDataRollbackHelper);
                 } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
@@ -675,6 +693,10 @@
      */
     private boolean enableRollback(
             int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "enableRollback user=" + user + " token=" + token
+                    + " path=" + newPackageCodePath.getAbsolutePath());
+        }
 
         // Find the session id associated with this install.
         // TODO: It would be nice if package manager or package installer told
@@ -838,6 +860,10 @@
     }
 
     private void snapshotUserDataInternal(String packageName, int[] userIds) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "snapshotUserData pkg=" + packageName
+                    + " users=" + Arrays.toString(userIds));
+        }
         synchronized (mLock) {
             // staged installs
             for (int i = 0; i < mRollbacks.size(); i++) {
@@ -854,6 +880,10 @@
 
     private void restoreUserDataInternal(
             String packageName, int[] userIds, int appId, String seInfo) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "restoreUserData pkg=" + packageName
+                    + " users=" + Arrays.toString(userIds));
+        }
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 Rollback rollback = mRollbacks.get(i);
@@ -1041,6 +1071,9 @@
 
         @Override
         public void onFinished(int sessionId, boolean success) {
+            if (LOCAL_LOGV) {
+                Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
+            }
             NewRollback newRollback;
             synchronized (mLock) {
                 newRollback = getNewRollbackForPackageSessionLocked(sessionId);
@@ -1070,6 +1103,10 @@
      */
     private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
         Rollback rollback = newRollback.rollback;
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "completeEnableRollback id="
+                    + rollback.info.getRollbackId() + " success=" + success);
+        }
         if (!success) {
             // The install session was aborted, clean up the pending install.
             rollback.delete(mAppDataRollbackHelper);
@@ -1108,6 +1145,9 @@
 
     @GuardedBy("rollback.getLock")
     private void makeRollbackAvailable(Rollback rollback) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "makeRollbackAvailable id=" + rollback.info.getRollbackId());
+        }
         rollback.makeAvailable();
 
         // TODO(zezeozue): Provide API to explicitly start observing instead
@@ -1280,6 +1320,11 @@
         final Rollback rollback;
         int parentSessionId = parentSession.getSessionId();
 
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "createNewRollback id=" + rollbackId
+                    + " user=" + userId + " installer=" + installerPackageName);
+        }
+
         if (parentSession.isStaged()) {
             rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
                     installerPackageName);
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index fcf87ee..7786833 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -165,7 +165,7 @@
         Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0);
         Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
         Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
-        Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+        Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 0);
         Settings.Global.putInt(
                 cr,
                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 3c79b23..0b970bf 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -23,10 +23,13 @@
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
+import android.util.LocalLog;
 import android.util.Slog;
 import android.util.TimestampedValue;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -34,13 +37,14 @@
 
 /**
  * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
- * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
- * this class can be assumed to be single threaded (though the thread used may vary).
+ * {@link AlarmManager}.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
-// @NotThreadSafe
 public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
 
-    private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
 
     @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
     @Retention(RetentionPolicy.SOURCE)
@@ -61,6 +65,9 @@
      */
     private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
 
+    // A log for changes made to the system clock and why.
+    @NonNull private final LocalLog mTimeChangesLog = new LocalLog(30);
+
     // @NonNull after initialize()
     private Callback mCallback;
 
@@ -80,7 +87,7 @@
     }
 
     @Override
-    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         // NITZ logic
 
         // Empty suggestions are just ignored as we don't currently keep track of suggestion origin.
@@ -103,7 +110,7 @@
     }
 
     @Override
-    public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
+    public synchronized void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
         final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
         setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion);
     }
@@ -116,7 +123,7 @@
                     newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
             if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
                 // Out of order or bogus.
-                Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+                Slog.w(LOG_TAG, "Bad NITZ signal received."
                         + " referenceTimeDifference=" + referenceTimeDifference
                         + " lastSuggestion=" + lastSuggestion
                         + " newSuggestion=" + newSuggestion);
@@ -126,6 +133,7 @@
         return true;
     }
 
+    @GuardedBy("this")
     private void setSystemClockIfRequired(
             @Origin int origin, TimestampedValue<Long> time, Object cause) {
         // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
@@ -140,16 +148,20 @@
             mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
 
             if (!mCallback.isAutoTimeDetectionEnabled()) {
-                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled."
-                        + " time=" + time
-                        + ", cause=" + cause);
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
+                            + " time=" + time
+                            + ", cause=" + cause);
+                }
                 return;
             }
         } else {
             if (mCallback.isAutoTimeDetectionEnabled()) {
-                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled."
-                        + " time=" + time
-                        + ", cause=" + cause);
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is enabled."
+                            + " time=" + time
+                            + ", cause=" + cause);
+                }
                 return;
             }
         }
@@ -167,7 +179,7 @@
                             mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
                     long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
                     if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
-                        Slog.w(TAG,
+                        Slog.w(LOG_TAG,
                                 "System clock has not tracked elapsed real time clock. A clock may"
                                         + " be inaccurate or something unexpectedly set the system"
                                         + " clock."
@@ -190,9 +202,10 @@
     }
 
     @Override
-    public void handleAutoTimeDetectionToggle(boolean enabled) {
+    public synchronized void handleAutoTimeDetectionChanged() {
         // If automatic time detection is enabled we update the system clock instantly if we can.
         // Conversely, if automatic time detection is disabled we leave the clock as it is.
+        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
         if (enabled) {
             if (mLastAutoSystemClockTime != null) {
                 // Only send the network broadcast if the last candidate would have caused one.
@@ -218,14 +231,27 @@
     }
 
     @Override
-    public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
         pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
         pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
         pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
         pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
                 + mLastAutoSystemClockTimeSendNetworkBroadcast);
+
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
+        ipw.println("TimeDetectorStrategyImpl logs:");
+        ipw.increaseIndent(); // level 1
+
+        ipw.println("Time change log:");
+        ipw.increaseIndent(); // level 2
+        mTimeChangesLog.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
+        ipw.decreaseIndent(); // level 1
     }
 
+    @GuardedBy("this")
     private void adjustAndSetDeviceSystemClock(
             TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
             long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) {
@@ -238,21 +264,27 @@
         long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
         long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
         if (absTimeDifference < systemClockUpdateThreshold) {
-            Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
-                    + " system clock are close enough."
-                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                    + " newTime=" + newTime
-                    + " cause=" + cause
-                    + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
-                    + " absTimeDifference=" + absTimeDifference);
+            if (DBG) {
+                Slog.d(LOG_TAG, "Not setting system clock. New time and"
+                        + " system clock are close enough."
+                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                        + " newTime=" + newTime
+                        + " cause=" + cause
+                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+                        + " absTimeDifference=" + absTimeDifference);
+            }
             return;
         }
 
-        Slog.d(TAG, "Setting system clock using time=" + newTime
+        mCallback.setSystemClock(newSystemClockMillis);
+        String logMsg = "Set system clock using time=" + newTime
                 + " cause=" + cause
                 + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                + " newTimeMillis=" + newSystemClockMillis);
-        mCallback.setSystemClock(newSystemClockMillis);
+                + " newSystemClockMillis=" + newSystemClockMillis;
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
+        }
+        mTimeChangesLog.log(logMsg);
 
         // CLOCK_PARANOIA : Record the last time this class set the system clock.
         mLastAutoSystemClockTimeSet = newTime;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0930975..34400ff 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -24,10 +24,9 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.os.Binder;
+import android.os.Handler;
 import android.provider.Settings;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
@@ -39,7 +38,7 @@
 import java.util.Objects;
 
 public final class TimeDetectorService extends ITimeDetectorService.Stub {
-    private static final String TAG = "timedetector.TimeDetectorService";
+    private static final String TAG = "TimeDetectorService";
 
     public static class Lifecycle extends SystemService {
 
@@ -57,29 +56,25 @@
         }
     }
 
+    @NonNull private final Handler mHandler;
     @NonNull private final Context mContext;
     @NonNull private final Callback mCallback;
-
-    // The lock used when call the strategy to ensure thread safety.
-    @NonNull private final Object mStrategyLock = new Object();
-
-    @GuardedBy("mStrategyLock")
     @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
 
     private static TimeDetectorService create(@NonNull Context context) {
-        final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
-        final TimeDetectorStrategyCallbackImpl callback =
-                new TimeDetectorStrategyCallbackImpl(context);
+        TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+        TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context);
         timeDetector.initialize(callback);
 
+        Handler handler = FgThread.getHandler();
         TimeDetectorService timeDetectorService =
-                new TimeDetectorService(context, callback, timeDetector);
+                new TimeDetectorService(context, handler, callback, timeDetector);
 
         // Wire up event listening.
         ContentResolver contentResolver = context.getContentResolver();
         contentResolver.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
-                new ContentObserver(FgThread.getHandler()) {
+                new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
                         timeDetectorService.handleAutoTimeDetectionToggle();
                     }
@@ -89,9 +84,10 @@
     }
 
     @VisibleForTesting
-    public TimeDetectorService(@NonNull Context context, @NonNull Callback callback,
-            @NonNull TimeDetectorStrategy timeDetectorStrategy) {
+    public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
+            @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
+        mHandler = Objects.requireNonNull(handler);
         mCallback = Objects.requireNonNull(callback);
         mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
     }
@@ -101,14 +97,7 @@
         enforceSuggestPhoneTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        long idToken = Binder.clearCallingIdentity();
-        try {
-            synchronized (mStrategyLock) {
-                mTimeDetectorStrategy.suggestPhoneTime(timeSignal);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(idToken);
-        }
+        mHandler.post(() -> mTimeDetectorStrategy.suggestPhoneTime(timeSignal));
     }
 
     @Override
@@ -116,22 +105,12 @@
         enforceSuggestManualTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        long idToken = Binder.clearCallingIdentity();
-        try {
-            synchronized (mStrategyLock) {
-                mTimeDetectorStrategy.suggestManualTime(timeSignal);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(idToken);
-        }
+        mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
     }
 
     @VisibleForTesting
     public void handleAutoTimeDetectionToggle() {
-        synchronized (mStrategyLock) {
-            final boolean timeDetectionEnabled = mCallback.isAutoTimeDetectionEnabled();
-            mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled);
-        }
+        mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
     }
 
     @Override
@@ -139,9 +118,7 @@
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        synchronized (mStrategyLock) {
-            mTimeDetectorStrategy.dump(pw, args);
-        }
+        mTimeDetectorStrategy.dump(pw, args);
     }
 
     private void enforceSuggestPhoneTimePermission() {
@@ -149,6 +126,6 @@
     }
 
     private void enforceSuggestManualTimePermission() {
-        mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SET_TIME, "set time");
     }
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index b60cebf..32cee2d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -27,12 +27,14 @@
 
 /**
  * The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations
- * of this interface can be assumed to be single threaded (though the thread used may vary).
+ * TimeDetectorService.
+ *
+ * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
+ * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
+ * handle thread safety.
  *
  * @hide
  */
-// @NotThreadSafe
 public interface TimeDetectorStrategy {
 
     /**
@@ -79,7 +81,7 @@
     void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
 
     /** Handle the auto-time setting being toggled on or off. */
-    void handleAutoTimeDetectionToggle(boolean enabled);
+    void handleAutoTimeDetectionChanged();
 
     /** Dump debug information. */
     void dump(@NonNull PrintWriter pw, @Nullable String[] args);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 23746ac..e034ad4 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -43,7 +43,7 @@
     }
 
     @Override
-    public boolean isTimeZoneDetectionEnabled() {
+    public boolean isAutoTimeZoneDetectionEnabled() {
         return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
     }
 
@@ -66,14 +66,16 @@
     }
 
     @Override
-    public void setDeviceTimeZone(String zoneId) {
+    public void setDeviceTimeZone(String zoneId, boolean sendNetworkBroadcast) {
         AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
         alarmManager.setTimeZone(zoneId);
 
-        // TODO Nothing in the platform appears to listen for this. Remove it.
-        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra("time-zone", zoneId);
-        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        if (sendNetworkBroadcast) {
+            // TODO Nothing in the platform appears to listen for this. Remove it.
+            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra("time-zone", zoneId);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 558aa9e..9a1fe65 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -28,7 +29,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
@@ -75,7 +75,7 @@
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                 new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
-                        timeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+                        timeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
                     }
                 });
 
@@ -91,8 +91,16 @@
     }
 
     @Override
+    public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+        enforceSuggestManualTimeZonePermission();
+        Objects.requireNonNull(timeZoneSuggestion);
+
+        mHandler.post(() -> mTimeZoneDetectorStrategy.suggestManualTimeZone(timeZoneSuggestion));
+    }
+
+    @Override
     public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
-        enforceSetTimeZonePermission();
+        enforceSuggestPhoneTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
         mHandler.post(() -> mTimeZoneDetectorStrategy.suggestPhoneTimeZone(timeZoneSuggestion));
@@ -103,13 +111,17 @@
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        mTimeZoneDetectorStrategy.dumpState(pw);
-        mTimeZoneDetectorStrategy.dumpLogs(new IndentingPrintWriter(pw, " "));
+        mTimeZoneDetectorStrategy.dumpState(pw, args);
     }
 
-    private void enforceSetTimeZonePermission() {
+    private void enforceSuggestPhoneTimeZonePermission() {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME_ZONE, "set time zone");
     }
+
+    private void enforceSuggestManualTimeZonePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+    }
 }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index e24c089..5db12c7 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -21,8 +21,10 @@
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.content.Context;
 import android.util.ArrayMap;
@@ -34,22 +36,34 @@
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Objects;
 
 /**
- * A singleton, stateful time zone detection strategy that is aware of multiple phone devices. It
- * keeps track of the most recent suggestion from each phone and it uses the best based on a scoring
- * algorithm. If several phones provide the same score then the phone with the lowest numeric ID
- * "wins". If the situation changes and it is no longer possible to be confident about the time
- * zone, phones must submit an empty suggestion in order to "withdraw" their previous suggestion.
+ * A singleton, stateful time zone detection strategy that is aware of user (manual) suggestions and
+ * suggestions from multiple phone devices. Suggestions are acted on or ignored as needed, dependent
+ * on the current "auto time zone detection" setting.
+ *
+ * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
+ * the best suggestion based on a scoring algorithm. If several phones provide the same score then
+ * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
+ * possible to be confident about the time zone, phones must submit an empty suggestion in order to
+ * "withdraw" their previous suggestion.
  */
 public class TimeZoneDetectorStrategy {
 
     /**
      * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
      * faked for tests.
+     *
+     * <p>Note: Because the system properties-derived values like
+     * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
+     * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
+     * processes!), their use are prone to race conditions. That will be true until the
+     * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategy}.
      */
     @VisibleForTesting
     public interface Callback {
@@ -57,7 +71,7 @@
         /**
          * Returns true if automatic time zone detection is enabled in settings.
          */
-        boolean isTimeZoneDetectionEnabled();
+        boolean isAutoTimeZoneDetectionEnabled();
 
         /**
          * Returns true if the device has had an explicit time zone set.
@@ -72,22 +86,34 @@
         /**
          * Sets the device's time zone.
          */
-        void setDeviceTimeZone(@NonNull String zoneId);
+        void setDeviceTimeZone(@NonNull String zoneId, boolean sendNetworkBroadcast);
     }
 
-    static final String LOG_TAG = "TimeZoneDetectorStrategy";
-    static final boolean DBG = false;
+    private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+    private static final boolean DBG = false;
+
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Origin {}
+
+    /** Used when a time value originated from a telephony signal. */
+    @Origin
+    private static final int ORIGIN_PHONE = 1;
+
+    /** Used when a time value originated from a user / manual settings. */
+    @Origin
+    private static final int ORIGIN_MANUAL = 2;
 
     /**
-     * The abstract score for an empty or invalid suggestion.
+     * The abstract score for an empty or invalid phone suggestion.
      *
-     * Used to score suggestions where there is no zone.
+     * Used to score phone suggestions where there is no zone.
      */
     @VisibleForTesting
-    public static final int SCORE_NONE = 0;
+    public static final int PHONE_SCORE_NONE = 0;
 
     /**
-     * The abstract score for a low quality suggestion.
+     * The abstract score for a low quality phone suggestion.
      *
      * Used to score suggestions where:
      * The suggested zone ID is one of several possibilities, and the possibilities have different
@@ -96,10 +122,10 @@
      * You would have to be quite desperate to want to use this choice.
      */
     @VisibleForTesting
-    public static final int SCORE_LOW = 1;
+    public static final int PHONE_SCORE_LOW = 1;
 
     /**
-     * The abstract score for a medium quality suggestion.
+     * The abstract score for a medium quality phone suggestion.
      *
      * Used for:
      * The suggested zone ID is one of several possibilities but at least the possibilities have the
@@ -107,33 +133,36 @@
      * switch to DST at the wrong time and (for example) their calendar events.
      */
     @VisibleForTesting
-    public static final int SCORE_MEDIUM = 2;
+    public static final int PHONE_SCORE_MEDIUM = 2;
 
     /**
-     * The abstract score for a high quality suggestion.
+     * The abstract score for a high quality phone suggestion.
      *
      * Used for:
      * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
      * the info available.
      */
     @VisibleForTesting
-    public static final int SCORE_HIGH = 3;
+    public static final int PHONE_SCORE_HIGH = 3;
 
     /**
-     * The abstract score for a highest quality suggestion.
+     * The abstract score for a highest quality phone suggestion.
      *
      * Used for:
      * Suggestions that must "win" because they constitute test or emulator zone ID.
      */
     @VisibleForTesting
-    public static final int SCORE_HIGHEST = 4;
+    public static final int PHONE_SCORE_HIGHEST = 4;
 
-    /** The threshold at which suggestions are good enough to use to set the device's time zone. */
+    /**
+     * The threshold at which phone suggestions are good enough to use to set the device's time
+     * zone.
+     */
     @VisibleForTesting
-    public static final int SCORE_USAGE_THRESHOLD = SCORE_MEDIUM;
+    public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
 
     /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
-    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
 
     @NonNull
     private final Callback mCallback;
@@ -146,24 +175,16 @@
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30);
 
     /**
-     * A mapping from phoneId to a linked list of time zone suggestions (the head being the latest).
-     * We typically expect one or two entries in this Map: devices will have a small number
+     * A mapping from phoneId to a linked list of phone time zone suggestions (the head being the
+     * latest). We typically expect one or two entries in this Map: devices will have a small number
      * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
-     * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+     * the ID will not exceed {@link #KEEP_PHONE_SUGGESTION_HISTORY_SIZE} in size.
      */
     @GuardedBy("this")
     private ArrayMap<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> mSuggestionByPhoneId =
             new ArrayMap<>();
 
     /**
-     * The most recent best guess of time zone from all phones. Can be {@code null} to indicate
-     * there would be no current suggestion.
-     */
-    @GuardedBy("this")
-    @Nullable
-    private QualifiedPhoneTimeZoneSuggestion mCurrentSuggestion;
-
-    /**
      * Creates a new instance of {@link TimeZoneDetectorStrategy}.
      */
     public static TimeZoneDetectorStrategy create(Context context) {
@@ -176,56 +197,68 @@
         mCallback = Objects.requireNonNull(callback);
     }
 
+    /** Process the suggested manually- / user-entered time zone. */
+    public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
+        Objects.requireNonNull(suggestion);
+
+        String timeZoneId = suggestion.getZoneId();
+        String cause = "Manual time suggestion received: suggestion=" + suggestion;
+        setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
+    }
+
     /**
      * Suggests a time zone for the device, or withdraws a previous suggestion if
      * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
      * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
      * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
-     * suggestion. The service uses suggestions to decide whether to modify the device's time zone
+     * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
      * setting and what to set it to.
      */
-    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion newSuggestion) {
+    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
         if (DBG) {
-            Slog.d(LOG_TAG, "suggestPhoneTimeZone: newSuggestion=" + newSuggestion);
+            Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
         }
-        Objects.requireNonNull(newSuggestion);
+        Objects.requireNonNull(suggestion);
 
-        int score = scoreSuggestion(newSuggestion);
+        // Score the suggestion.
+        int score = scorePhoneSuggestion(suggestion);
         QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(newSuggestion, score);
+                new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
 
-        // Record the suggestion against the correct phoneId.
+        // Store the suggestion against the correct phoneId.
         LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
-                mSuggestionByPhoneId.get(newSuggestion.getPhoneId());
+                mSuggestionByPhoneId.get(suggestion.getPhoneId());
         if (suggestions == null) {
             suggestions = new LinkedList<>();
-            mSuggestionByPhoneId.put(newSuggestion.getPhoneId(), suggestions);
+            mSuggestionByPhoneId.put(suggestion.getPhoneId(), suggestions);
         }
         suggestions.addFirst(scoredSuggestion);
-        if (suggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+        if (suggestions.size() > KEEP_PHONE_SUGGESTION_HISTORY_SIZE) {
             suggestions.removeLast();
         }
 
-        // Now run the competition between the phones' suggestions.
-        doTimeZoneDetection();
+        // Now perform auto time zone detection. The new suggestion may be used to modify the time
+        // zone setting.
+        String reason = "New phone time suggested. suggestion=" + suggestion;
+        doAutoTimeZoneDetection(reason);
     }
 
-    private static int scoreSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+    private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
-            score = SCORE_NONE;
+            score = PHONE_SCORE_NONE;
         } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
                 || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
             // Handle emulator / test cases : These suggestions should always just be used.
-            score = SCORE_HIGHEST;
+            score = PHONE_SCORE_HIGHEST;
         } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
-            score = SCORE_HIGH;
+            score = PHONE_SCORE_HIGH;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
             // The suggestion may be wrong, but at least the offset should be correct.
-            score = SCORE_MEDIUM;
+            score = PHONE_SCORE_MEDIUM;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
             // The suggestion has a good chance of being wrong.
-            score = SCORE_LOW;
+            score = PHONE_SCORE_LOW;
         } else {
             throw new AssertionError();
         }
@@ -235,47 +268,46 @@
     /**
      * Finds the best available time zone suggestion from all phones. If it is high-enough quality
      * and automatic time zone detection is enabled then it will be set on the device. The outcome
-     * can be that this service becomes / remains un-opinionated and nothing is set.
+     * can be that this strategy becomes / remains un-opinionated and nothing is set.
      */
     @GuardedBy("this")
-    private void doTimeZoneDetection() {
-        QualifiedPhoneTimeZoneSuggestion bestSuggestion = findBestSuggestion();
-        boolean timeZoneDetectionEnabled = mCallback.isTimeZoneDetectionEnabled();
+    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
+        if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+            // Avoid doing unnecessary work with this (race-prone) check.
+            return;
+        }
+
+        QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
 
         // Work out what to do with the best suggestion.
-        if (bestSuggestion == null) {
-            // There is no suggestion. Become un-opinionated.
+        if (bestPhoneSuggestion == null) {
+            // There is no phone suggestion available at all. Become un-opinionated.
             if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: No good suggestion."
-                        + " bestSuggestion=null"
-                        + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+                Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+                        + " detectionReason=" + detectionReason);
             }
-            mCurrentSuggestion = null;
             return;
         }
 
         // Special case handling for uninitialized devices. This should only happen once.
-        String newZoneId = bestSuggestion.suggestion.getZoneId();
+        String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
         if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
-            Slog.i(LOG_TAG, "doTimeZoneDetection: Device has no time zone set so might set the"
-                    + " device to the best available suggestion."
-                    + " bestSuggestion=" + bestSuggestion
-                    + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
-
-            mCurrentSuggestion = bestSuggestion;
-            if (timeZoneDetectionEnabled) {
-                setDeviceTimeZone(bestSuggestion.suggestion);
-            }
+            String cause = "Device has no time zone set. Attempting to set the device to the best"
+                    + " available suggestion."
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + ", detectionReason=" + detectionReason;
+            Slog.i(LOG_TAG, cause);
+            setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
             return;
         }
 
-        boolean suggestionGoodEnough = bestSuggestion.score >= SCORE_USAGE_THRESHOLD;
+        boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
         if (!suggestionGoodEnough) {
             if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: Suggestion not good enough."
-                        + " bestSuggestion=" + bestSuggestion);
+                Slog.d(LOG_TAG, "Best suggestion not good enough."
+                        + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                        + ", detectionReason=" + detectionReason);
             }
-            mCurrentSuggestion = null;
             return;
         }
 
@@ -283,63 +315,84 @@
         // zone ID.
         if (newZoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
-                    + " bestSuggestion=" + bestSuggestion);
-            mCurrentSuggestion = null;
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + " detectionReason=" + detectionReason);
             return;
         }
 
-        // There is a good suggestion. Store the suggestion and set the device time zone if
-        // settings allow.
-        mCurrentSuggestion = bestSuggestion;
-
-        // Only set the device time zone if time zone detection is enabled.
-        if (!timeZoneDetectionEnabled) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: Not setting the time zone because time zone"
-                        + " detection is disabled."
-                        + " bestSuggestion=" + bestSuggestion);
-            }
-            return;
-        }
-        PhoneTimeZoneSuggestion suggestion = bestSuggestion.suggestion;
-        setDeviceTimeZone(suggestion);
+        String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        String cause = "Found good suggestion."
+                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", detectionReason=" + detectionReason;
+        setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
     }
 
-    private void setDeviceTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
-        String currentZoneId = mCallback.getDeviceTimeZone();
-        String newZoneId = suggestion.getZoneId();
+    @GuardedBy("this")
+    private void setDeviceTimeZoneIfRequired(
+            @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
+        Objects.requireNonNull(newZoneId);
+        Objects.requireNonNull(cause);
 
-        // Paranoia: This should never happen.
-        if (newZoneId == null) {
-            Slog.w(LOG_TAG, "setDeviceTimeZone: Suggested zone is null."
-                    + " timeZoneSuggestion=" + suggestion);
-            return;
+        boolean sendNetworkBroadcast = (origin == ORIGIN_PHONE);
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        if (isOriginAutomatic) {
+            if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        } else {
+            if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
         }
 
+        String currentZoneId = mCallback.getDeviceTimeZone();
+
         // Avoid unnecessary changes / intents.
         if (newZoneId.equals(currentZoneId)) {
             // No need to set the device time zone - the setting is already what we would be
             // suggesting.
             if (DBG) {
-                Slog.d(LOG_TAG, "setDeviceTimeZone: No need to change the time zone;"
+                Slog.d(LOG_TAG, "No need to change the time zone;"
                         + " device is already set to the suggested zone."
-                        + " timeZoneSuggestion=" + suggestion);
+                        + " origin=" + origin
+                        + ", newZoneId=" + newZoneId
+                        + ", cause=" + cause);
             }
             return;
         }
 
-        String msg = "Changing device time zone. currentZoneId=" + currentZoneId
-                + ", timeZoneSuggestion=" + suggestion;
+        mCallback.setDeviceTimeZone(newZoneId, sendNetworkBroadcast);
+        String msg = "Set device time zone."
+                + " origin=" + origin
+                + ", currentZoneId=" + currentZoneId
+                + ", newZoneId=" + newZoneId
+                + ", sendNetworkBroadcast" + sendNetworkBroadcast
+                + ", cause=" + cause;
         if (DBG) {
             Slog.d(LOG_TAG, msg);
         }
         mTimeZoneChangesLog.log(msg);
-        mCallback.setDeviceTimeZone(newZoneId);
+    }
+
+    private static boolean isOriginAutomatic(@Origin int origin) {
+        return origin == ORIGIN_PHONE;
     }
 
     @GuardedBy("this")
     @Nullable
-    private QualifiedPhoneTimeZoneSuggestion findBestSuggestion() {
+    private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
         QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
 
         // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
@@ -376,38 +429,44 @@
     }
 
     /**
-     * Returns the current best suggestion. Not intended for general use: it is used during tests
-     * to check service behavior.
+     * Returns the current best phone suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
      */
     @VisibleForTesting
     @Nullable
-    public synchronized QualifiedPhoneTimeZoneSuggestion findBestSuggestionForTests() {
-        return findBestSuggestion();
+    public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
+        return findBestPhoneSuggestion();
     }
 
     /**
-     * Called when the has been a change to the automatic time zone detection setting.
+     * Called when there has been a change to the automatic time zone detection setting.
      */
     @VisibleForTesting
-    public synchronized void handleTimeZoneDetectionChange() {
+    public synchronized void handleAutoTimeZoneDetectionChange() {
         if (DBG) {
             Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
         }
-        if (mCallback.isTimeZoneDetectionEnabled()) {
+        if (mCallback.isAutoTimeZoneDetectionEnabled()) {
             // When the user enabled time zone detection, run the time zone detection and change the
             // device time zone if possible.
-            doTimeZoneDetection();
+            String reason = "Auto time zone detection setting enabled.";
+            doAutoTimeZoneDetection(reason);
         }
     }
 
     /**
-     * Dumps any logs held to the supplied writer.
+     * Dumps internal state such as field values.
      */
-    public synchronized void dumpLogs(IndentingPrintWriter ipw) {
-        ipw.println("TimeZoneDetectorStrategy:");
+    public synchronized void dumpState(PrintWriter pw, String[] args) {
+        pw.println("TimeZoneDetectorStrategy:");
+        pw.println("mCallback.isTimeZoneDetectionEnabled()="
+                + mCallback.isAutoTimeZoneDetectionEnabled());
+        pw.println("mCallback.isDeviceTimeZoneInitialized()="
+                + mCallback.isDeviceTimeZoneInitialized());
+        pw.println("mCallback.getDeviceTimeZone()="
+                + mCallback.getDeviceTimeZone());
 
-        ipw.increaseIndent(); // level 1
-
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
         ipw.println("Time zone change log:");
         ipw.increaseIndent(); // level 2
         mTimeZoneChangesLog.dump(ipw);
@@ -427,24 +486,13 @@
         }
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
-    }
+        ipw.flush();
 
-    /**
-     * Dumps internal state such as field values.
-     */
-    public synchronized void dumpState(PrintWriter pw) {
-        pw.println("mCurrentSuggestion=" + mCurrentSuggestion);
-        pw.println("mCallback.isTimeZoneDetectionEnabled()="
-                + mCallback.isTimeZoneDetectionEnabled());
-        pw.println("mCallback.isDeviceTimeZoneInitialized()="
-                + mCallback.isDeviceTimeZoneInitialized());
-        pw.println("mCallback.getDeviceTimeZone()="
-                + mCallback.getDeviceTimeZone());
         pw.flush();
     }
 
     /**
-     * A method used to inspect service state during tests. Not intended for general use.
+     * A method used to inspect strategy state during tests. Not intended for general use.
      */
     @VisibleForTesting
     public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 45e3c68..db94cf1 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -73,7 +73,9 @@
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
@@ -617,7 +619,7 @@
                 continue;
             }
 
-            stack.findTaskLocked(r, mTmpFindTaskResult);
+            mTmpFindTaskResult.process(r, stack);
             // It is possible to have tasks in multiple stacks with the same root affinity, so
             // we should keep looking after finding an affinity match to see if there is a
             // better match in another stack. Also, task affinity isn't a good enough reason
@@ -1190,12 +1192,11 @@
     }
 
     boolean isUidPresent(int uid) {
-        for (ActivityStack stack : mStacks) {
-            if (stack.isUidPresent(uid)) {
-                return true;
-            }
-        }
-        return false;
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                ActivityRecord::isUid, PooledLambda.__(ActivityRecord.class), uid);
+        final boolean isUidPresent = mDisplayContent.getActivity(p) != null;
+        p.recycle();
+        return isUidPresent;
     }
 
     /**
@@ -1291,12 +1292,16 @@
     /** Update and get all UIDs that are present on the display and have access to it. */
     IntArray getPresentUIDs() {
         mDisplayAccessUIDs.clear();
-        for (ActivityStack stack : mStacks) {
-            stack.getPresentUIDs(mDisplayAccessUIDs);
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(ActivityDisplay::addActivityUid,
+                PooledLambda.__(ActivityRecord.class), mDisplayAccessUIDs);
+        mDisplayContent.forAllActivities(c);
+        c.recycle();
         return mDisplayAccessUIDs;
     }
 
+    private static void addActivityUid(ActivityRecord r, IntArray uids) {
+        uids.add(r.getUid());
+    }
     /**
      * Checks if system decorations should be shown on this display.
      *
@@ -1449,22 +1454,16 @@
             return null;
         }
 
-        final ArrayList<Task> tasks = mHomeStack.getAllTasks();
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = tasks.get(taskNdx);
-            if (!task.isActivityTypeHome()) {
-                continue;
-            }
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                ActivityDisplay::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
+                userId);
+        final ActivityRecord r = mHomeStack.getActivity(p);
+        p.recycle();
+        return r;
+    }
 
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.isActivityTypeHome()
-                        && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
-                    return r;
-                }
-            }
-        }
-        return null;
+    private static boolean isHomeActivityForUser(ActivityRecord r, int userId) {
+        return r.isActivityTypeHome() && (userId == UserHandle.USER_ALL || r.mUserId == userId);
     }
 
     boolean isSleeping() {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 9fa5d9f1..de51d4b 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -64,25 +64,24 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.WaitResult;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.dex.ArtManagerInternal;
 import android.content.pm.dex.PackageOptimizationInfo;
 import android.metrics.LogMaker;
-import android.os.Handler;
+import android.os.Binder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -90,16 +89,23 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 
+import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.concurrent.TimeUnit;
 
 /**
  * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
  * determine app launch times and draw delays. Source of truth for activity metrics and provides
  * data for Tron, logcat, event logs and {@link android.app.WaitResult}.
- *
+ * <p>
+ * A typical sequence of a launch event could be:
+ * {@link #notifyActivityLaunching}, {@link #notifyActivityLaunched},
+ * {@link #notifyStartingWindowDrawn} (optional), {@link #notifyTransitionStarting}
+ * {@link #notifyWindowsDrawn}.
+ * <p>
  * Tests:
  * atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests
  */
@@ -115,12 +121,14 @@
     private static final int WINDOW_STATE_ASSISTANT = 3;
     private static final int WINDOW_STATE_INVALID = -1;
 
-    private static final long INVALID_START_TIME = -1;
+    /**
+     * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active
+     * 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;
     private static final int INVALID_TRANSITION_TYPE = -1;
 
-    private static final int MSG_CHECK_VISIBILITY = 0;
-
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
     private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
@@ -129,27 +137,12 @@
     private int mWindowState = WINDOW_STATE_STANDARD;
     private long mLastLogTimeSecs;
     private final ActivityStackSupervisor mSupervisor;
-    private final Context mContext;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
-    // set to INVALID_START_TIME in reset.
-    // set to valid value in notifyActivityLaunching
-    private long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
-    private long mLastTransitionStartTimeNs = INVALID_START_TIME;
-
-    private int mCurrentTransitionDeviceUptime;
-    private int mCurrentTransitionDelayMs;
-
-    /** If the any app transitions have been logged as starting, after the latest reset. */
-    private boolean mLoggedTransitionStarting;
-
-    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
-    private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
-            new SparseArray<>();
-    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
-    private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
-            new SparseArray<>();
-    private final H mHandler;
+    /** All active transitions. */
+    private final ArrayList<TransitionInfo> mTransitionInfoList = new ArrayList<>();
+    /** Map : Last launched activity => {@link TransitionInfo} */
+    private final ArrayMap<ActivityRecord, TransitionInfo> mLastTransitionInfo = new ArrayMap<>();
 
     private ArtManagerInternal mArtManagerInternal;
     private final StringBuilder mStringBuilder = new StringBuilder();
@@ -161,54 +154,151 @@
     private final LaunchObserverRegistryImpl mLaunchObserver;
     @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
 
-    private final class H extends Handler {
+    /**
+     * The information created when an intent is incoming but we do not yet know whether it will be
+     * launched successfully.
+     */
+    static final class LaunchingState {
+        /** The timestamp of {@link #notifyActivityLaunching}. */
+        private long mCurrentTransitionStartTimeNs;
+        /** Non-null when a {@link TransitionInfo} is created for this state. */
+        private TransitionInfo mAssociatedTransitionInfo;
 
-        public H(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CHECK_VISIBILITY:
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    checkVisibility((Task) args.arg1, (ActivityRecord) args.arg2);
-                    break;
-            }
+        @VisibleForTesting
+        boolean allDrawn() {
+            return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
         }
     }
 
-    private final class WindowingModeTransitionInfo {
+    /** The information created when an activity is confirmed to be launched. */
+    private static final class TransitionInfo {
+        /**
+         * The field to lookup and update an existing transition efficiently between
+         * {@link #notifyActivityLaunching} and {@link #notifyActivityLaunched}.
+         *
+         * @see LaunchingState#mAssociatedTransitionInfo
+         */
+        final LaunchingState mLaunchingState;
+        /**
+         * The timestamp of the first {@link #notifyActivityLaunching}. It can be used as a key for
+         * observer to identify which callbacks belong to a launch event.
+         */
+        final long mTransitionStartTimeNs;
+        /** The device uptime in seconds when this transition info is created. */
+        final int mCurrentTransitionDeviceUptime;
+        /** The type can be cold (new process), warm (new activity), or hot (bring to front). */
+        final int mTransitionType;
+        /** Whether the process was already running when the transition started. */
+        final boolean mProcessRunning;
+        /** The activities that should be drawn. */
+        final LinkedList<ActivityRecord> mPendingDrawActivities = new LinkedList<>();
         /** The latest activity to have been launched. */
-        private ActivityRecord launchedActivity;
-        private int startResult;
-        private boolean currentTransitionProcessRunning;
+        @NonNull ActivityRecord mLastLaunchedActivity;
+
+        /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyTransitionStarting}. */
+        int mCurrentTransitionDelayMs;
+        /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyStartingWindowDrawn}. */
+        int mStartingWindowDelayMs = INVALID_DELAY;
+        /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyBindApplication}. */
+        int mBindApplicationDelayMs = INVALID_DELAY;
         /** Elapsed time from when we launch an activity to when its windows are drawn. */
-        private int windowsDrawnDelayMs;
-        private int startingWindowDelayMs = INVALID_DELAY;
-        private int bindApplicationDelayMs = INVALID_DELAY;
-        private int reason = APP_TRANSITION_TIMEOUT;
-        // TODO(b/132736359) The number may need to consider the visibility change.
-        private int numUndrawnActivities = 1;
+        int mWindowsDrawnDelayMs;
+        /** The reason why the transition started (see ActivityManagerInternal.APP_TRANSITION_*). */
+        int mReason = APP_TRANSITION_TIMEOUT;
+        /** The flag ensures that {@link #mStartingWindowDelayMs} is only set once. */
+        boolean mLoggedStartingWindowDrawn;
+        /** If the any app transitions have been logged as starting. */
+        boolean mLoggedTransitionStarting;
+
         /** Non-null if the application has reported drawn but its window hasn't. */
-        private Runnable pendingFullyDrawn;
-        private boolean loggedStartingWindowDrawn;
-        private boolean launchTraceActive;
+        @Nullable Runnable mPendingFullyDrawn;
+        /** Non-null if the trace is active. */
+        @Nullable String mLaunchTraceName;
+
+        /** @return Non-null if there will be a window drawn event for the launch. */
+        @Nullable
+        static TransitionInfo create(@NonNull ActivityRecord r,
+                @NonNull LaunchingState launchingState, boolean processRunning, int startResult) {
+            int transitionType = INVALID_TRANSITION_TYPE;
+            if (processRunning) {
+                if (startResult == START_SUCCESS) {
+                    transitionType = TYPE_TRANSITION_WARM_LAUNCH;
+                } else if (startResult == START_TASK_TO_FRONT) {
+                    transitionType = TYPE_TRANSITION_HOT_LAUNCH;
+                }
+            } else if (startResult == START_SUCCESS || startResult == START_TASK_TO_FRONT) {
+                // Task may still exist when cold launching an activity and the start result will be
+                // set to START_TASK_TO_FRONT. Treat this as a COLD launch.
+                transitionType = TYPE_TRANSITION_COLD_LAUNCH;
+            }
+            if (transitionType == INVALID_TRANSITION_TYPE) {
+                // That means the startResult is neither START_SUCCESS nor START_TASK_TO_FRONT.
+                return null;
+            }
+            return new TransitionInfo(r, launchingState, transitionType, processRunning);
+        }
+
+        /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */
+        private TransitionInfo(ActivityRecord r, LaunchingState launchingState, int transitionType,
+                boolean processRunning) {
+            mLaunchingState = launchingState;
+            mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
+            mTransitionType = transitionType;
+            mProcessRunning = processRunning;
+            mCurrentTransitionDeviceUptime =
+                    (int) TimeUnit.MILLISECONDS.toSeconds(SystemClock.uptimeMillis());
+            setLatestLaunchedActivity(r);
+            launchingState.mAssociatedTransitionInfo = this;
+        }
 
         /**
          * Remembers the latest launched activity to represent the final transition. This also
-         * increments the number of activities that should be drawn, so a consecutive launching
-         * sequence can be coalesced as one event.
+         * tracks the activities that should be drawn, so a consecutive launching sequence can be
+         * coalesced as one event.
          */
         void setLatestLaunchedActivity(ActivityRecord r) {
-            if (launchedActivity == r) {
+            if (mLastLaunchedActivity == r) {
                 return;
             }
-            launchedActivity = r;
+            mLastLaunchedActivity = r;
+            if (!r.noDisplay) {
+                if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
+                mPendingDrawActivities.add(r);
+            }
+        }
+
+        /** @return {@code true} if the activity matches a launched activity in this transition. */
+        boolean contains(ActivityRecord r) {
+            return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
+        }
+
+        /** Called when the activity is drawn or won't be drawn. */
+        void removePendingDrawActivity(ActivityRecord r) {
+            if (DEBUG_METRICS) Slog.i(TAG, "Remove pending draw " + r);
+            mPendingDrawActivities.remove(r);
+        }
+
+        boolean allDrawn() {
+            return mPendingDrawActivities.isEmpty();
+        }
+
+        int calculateCurrentDelay() {
+            return calculateDelay(SystemClock.elapsedRealtimeNanos());
+        }
+
+        int calculateDelay(long timestampNs) {
+            // Shouldn't take more than 25 days to launch an app, so int is fine here.
+            return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs - mTransitionStartTimeNs);
+        }
+
+        @Override
+        public String toString() {
+            return "TransitionInfo{" + Integer.toHexString(System.identityHashCode(this))
+                    + " a=" + mLastLaunchedActivity + " ua=" + mPendingDrawActivities + "}";
         }
     }
 
-    final class WindowingModeTransitionInfoSnapshot {
+    static final class TransitionInfoSnapshot {
         final private ApplicationInfo applicationInfo;
         final private WindowProcessController processRecord;
         final String packageName;
@@ -231,17 +321,12 @@
         final int windowsFullyDrawnDelayMs;
         final int activityRecordIdHashCode;
 
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
-            this(info, info.launchedActivity);
+        private TransitionInfoSnapshot(TransitionInfo info) {
+            this(info, info.mLastLaunchedActivity, INVALID_DELAY);
         }
 
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
-                ActivityRecord launchedActivity) {
-            this(info, launchedActivity, INVALID_DELAY);
-        }
-
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
-                ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) {
+        private TransitionInfoSnapshot(TransitionInfo info, ActivityRecord launchedActivity,
+                int windowsFullyDrawnDelayMs) {
             applicationInfo = launchedActivity.info.applicationInfo;
             packageName = launchedActivity.packageName;
             launchedActivityName = launchedActivity.info.name;
@@ -250,12 +335,12 @@
             launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
                     ? null
                     : launchedActivity.app.getRequiredAbi();
-            reason = info.reason;
-            startingWindowDelayMs = info.startingWindowDelayMs;
-            bindApplicationDelayMs = info.bindApplicationDelayMs;
-            windowsDrawnDelayMs = info.windowsDrawnDelayMs;
-            type = getTransitionType(info);
-            processRecord = findProcessForActivity(launchedActivity);
+            reason = info.mReason;
+            startingWindowDelayMs = info.mStartingWindowDelayMs;
+            bindApplicationDelayMs = info.mBindApplicationDelayMs;
+            windowsDrawnDelayMs = info.mWindowsDrawnDelayMs;
+            type = info.mTransitionType;
+            processRecord = launchedActivity.app;
             processName = launchedActivity.processName;
             userId = launchedActivity.mUserId;
             launchedActivityShortComponentName = launchedActivity.shortComponentName;
@@ -277,11 +362,9 @@
         }
     }
 
-    ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
+    ActivityMetricsLogger(ActivityStackSupervisor supervisor, Looper looper) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
-        mContext = context;
-        mHandler = new H(looper);
         mLaunchObserver = new LaunchObserverRegistryImpl(looper);
     }
 
@@ -291,7 +374,7 @@
             // We log even if the window state hasn't changed, because the user might remain in
             // home/fullscreen move forever and we would like to track this kind of behavior
             // too.
-            MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
+            mMetricsLogger.count(TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
                     (int) (now - mLastLogTimeSecs));
         }
         mLastLogTimeSecs = now;
@@ -332,145 +415,169 @@
         }
     }
 
+    /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
+    @Nullable
+    private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
+        for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+            final TransitionInfo info = mTransitionInfoList.get(i);
+            if (info.contains(r)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * This method should be only used by starting recents and starting from recents, or internal
+     * tests. Because it doesn't lookup caller and always creates a new launching state.
+     *
+     * @see #notifyActivityLaunching(Intent, ActivityRecord, int)
+     */
+    LaunchingState notifyActivityLaunching(Intent intent) {
+        return notifyActivityLaunching(intent, null /* caller */, IGNORE_CALLER);
+    }
+
+    /**
+     * If the caller is found in an active transition, it will be considered as consecutive launch
+     * and coalesced into the active transition.
+     *
+     * @see #notifyActivityLaunching(Intent, ActivityRecord, int)
+     */
+    LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller) {
+        return notifyActivityLaunching(intent, caller, Binder.getCallingUid());
+    }
+
     /**
      * Notifies the tracker at the earliest possible point when we are starting to launch an
-     * activity.
+     * activity. The caller must ensure that {@link #notifyActivityLaunched} will be called later
+     * with the returned {@link LaunchingState}.
      */
-    void notifyActivityLaunching(Intent intent) {
+    private LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller,
+            int callingUid) {
+        final long transitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+        TransitionInfo existingInfo = null;
+        if (callingUid != IGNORE_CALLER) {
+            // Associate the launching event to an active transition if the caller is found in its
+            // launched activities.
+            for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+                final TransitionInfo info = mTransitionInfoList.get(i);
+                if (caller != null && info.contains(caller)) {
+                    existingInfo = info;
+                    break;
+                }
+                if (existingInfo == null && callingUid == info.mLastLaunchedActivity.getUid()) {
+                    // Fallback to check the most recent matched uid for the case that the caller is
+                    // not an activity.
+                    existingInfo = info;
+                }
+            }
+        }
         if (DEBUG_METRICS) {
-            Slog.i(TAG, String.format("notifyActivityLaunching: active:%b, intent:%s",
-                                      isAnyTransitionActive(),
-                                      intent));
+            Slog.i(TAG, "notifyActivityLaunching intent=" + intent
+                    + " existingInfo=" + existingInfo);
         }
 
-        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
-
-            mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
-            mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
-
-            launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
+        if (existingInfo == null) {
+            // Only notify the observer for a new launching event.
+            launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
+            final LaunchingState launchingState = new LaunchingState();
+            launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
+            return launchingState;
         }
+        existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
+        return existingInfo.mLaunchingState;
     }
 
     /**
      * Notifies the tracker that the activity is actually launching.
      *
-     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
-     *                   launch
-     * @param launchedActivity the activity that is being launched
+     * @param launchingState The launching state to track the new or active transition.
+     * @param resultCode One of the {@link android.app.ActivityManager}.START_* flags, indicating
+     *                   the result of the launch.
+     * @param launchedActivity The activity that is being launched
      */
-    void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
-        final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
-        final boolean processRunning = processRecord != null;
+    void notifyActivityLaunched(@NonNull LaunchingState launchingState, int resultCode,
+            @Nullable ActivityRecord launchedActivity) {
+        if (launchedActivity == null) {
+            // The launch is aborted, e.g. intent not resolved, class not found.
+            abort(null /* info */, "nothing launched");
+            return;
+        }
 
+        final WindowProcessController processRecord = launchedActivity.app != null
+                ? launchedActivity.app
+                : mSupervisor.mService.getProcessController(
+                        launchedActivity.processName, launchedActivity.info.applicationInfo.uid);
+        // Whether the process that will contains the activity is already running.
+        final boolean processRunning = processRecord != null;
         // We consider this a "process switch" if the process of the activity that gets launched
         // didn't have an activity that was in started state. In this case, we assume that lot
         // of caches might be purged so the time until it produces the first frame is very
         // interesting.
-        final boolean processSwitch = processRecord == null
+        final boolean processSwitch = !processRunning
                 || !processRecord.hasStartedActivity(launchedActivity);
 
-        notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
-    }
-
-    /**
-     * Notifies the tracker the the activity is actually launching.
-     *
-     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
-     *                   launch
-     * @param launchedActivity the activity being launched
-     * @param processRunning whether the process that will contains the activity is already running
-     * @param processSwitch whether the process that will contain the activity didn't have any
-     *                      activity that was stopped, i.e. the started activity is "switching"
-     *                      processes
-     */
-    private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
-            boolean processRunning, boolean processSwitch) {
-
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched"
-                + " resultCode=" + resultCode
-                + " launchedActivity=" + launchedActivity
-                + " processRunning=" + processRunning
-                + " processSwitch=" + processSwitch);
-
-        // If we are already in an existing transition, only update the activity name, but not the
-        // other attributes.
-        final @WindowingMode int windowingMode = launchedActivity != null
-                ? launchedActivity.getWindowingMode()
-                : WINDOWING_MODE_UNDEFINED;
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
-            // No transition is active ignore this launch.
-            return;
+        final TransitionInfo info = launchingState.mAssociatedTransitionInfo;
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, "notifyActivityLaunched" + " resultCode=" + resultCode
+                    + " launchedActivity=" + launchedActivity + " processRunning=" + processRunning
+                    + " processSwitch=" + processSwitch + " info=" + info);
         }
 
-        if (launchedActivity != null && launchedActivity.mDrawn) {
+        if (launchedActivity.mDrawn) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
             abort(info, "launched activity already visible");
             return;
         }
 
-        if (launchedActivity != null && info != null) {
+        if (info != null) {
             // If we are already in an existing transition, only update the activity name, but not
             // the other attributes.
 
+            if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched update launched activity");
             // Coalesce multiple (trampoline) activities from a single sequence together.
             info.setLatestLaunchedActivity(launchedActivity);
             return;
         }
 
-        final boolean otherWindowModesLaunching =
-                mWindowingModeTransitionInfo.size() > 0 && info == null;
-        if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
-                || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
-            // Failed to launch or it was not a process switch, so we don't care about the timing.
-            abort(info, "failed to launch or not a process switch");
+        if (!processSwitch) {
+            abort(info, "not a process switch");
             return;
-        } else if (otherWindowModesLaunching) {
-            // Don't log this windowing mode but continue with the other windowing modes.
+        }
+
+        final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
+                processRunning, resultCode);
+        if (newInfo == null) {
+            abort(info, "unrecognized launch");
             return;
         }
 
         if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
-
-        // A new launch sequence [with the windowingMode] has begun.
-        // Start tracking it.
-        final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
-        newInfo.setLatestLaunchedActivity(launchedActivity);
-        newInfo.currentTransitionProcessRunning = processRunning;
-        newInfo.startResult = resultCode;
-        mWindowingModeTransitionInfo.put(windowingMode, newInfo);
-        mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
-        mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
-        startTraces(newInfo);
+        // A new launch sequence has begun. Start tracking it.
+        mTransitionInfoList.add(newInfo);
+        mLastTransitionInfo.put(launchedActivity, newInfo);
+        startLaunchTrace(newInfo);
         launchObserverNotifyActivityLaunched(newInfo);
     }
 
     /**
-     * @return True if we should start logging an event for an activity start that returned
-     *         {@code resultCode} and that we'll indeed get a windows drawn event.
-     */
-    private boolean isLoggableResultCode(int resultCode) {
-        return resultCode == START_SUCCESS || resultCode == START_TASK_TO_FRONT;
-    }
-
-    /**
      * Notifies the tracker that all windows of the app have been drawn.
      */
-    WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
-                                                           long timestampNs) {
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
+    @Nullable
+    TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);
 
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (info == null || info.numUndrawnActivities == 0) {
+        final TransitionInfo info = getActiveTransitionInfo(r);
+        if (info == null || info.allDrawn()) {
+            if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn no activity to be drawn");
             return null;
         }
-        info.windowsDrawnDelayMs = calculateDelay(timestampNs);
-        info.numUndrawnActivities--;
-        final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                new WindowingModeTransitionInfoSnapshot(info);
-        if (allWindowsDrawn() && mLoggedTransitionStarting) {
-            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
+        // Always calculate the delay because the caller may need to know the individual drawn time.
+        info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
+        info.removePendingDrawActivity(r);
+        final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
+        if (info.mLoggedTransitionStarting && info.allDrawn()) {
+            done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
         }
         return infoSnapshot;
     }
@@ -478,88 +585,87 @@
     /**
      * Notifies the tracker that the starting window was drawn.
      */
-    void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestampNs) {
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (info == null || info.loggedStartingWindowDrawn) {
+    void notifyStartingWindowDrawn(@NonNull ActivityRecord r) {
+        final TransitionInfo info = getActiveTransitionInfo(r);
+        if (info == null || info.mLoggedStartingWindowDrawn) {
             return;
         }
-        info.loggedStartingWindowDrawn = true;
-        info.startingWindowDelayMs = calculateDelay(timestampNs);
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyStartingWindowDrawn " + r);
+        info.mLoggedStartingWindowDrawn = true;
+        info.mStartingWindowDelayMs = info.calculateDelay(SystemClock.elapsedRealtimeNanos());
     }
 
     /**
      * Notifies the tracker that the app transition is starting.
      *
-     * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
-     *                              of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
+     * @param activityToReason A map from activity to a reason integer, which must be on of
+     *                         ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
      */
-    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
-        if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
-            // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
-
-            // Ignore any subsequent notifyTransitionStarting until the next reset.
-            return;
-        }
+    void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
-        mCurrentTransitionDelayMs = calculateDelay(timestampNs);
-        mLoggedTransitionStarting = true;
 
-        WindowingModeTransitionInfo foundInfo = null;
-        for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
-            final @WindowingMode int windowingMode = windowingModeToReason.keyAt(index);
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                    windowingMode);
-            if (info == null) {
+        final long timestampNs = SystemClock.elapsedRealtimeNanos();
+        for (int index = activityToReason.size() - 1; index >= 0; index--) {
+            final ActivityRecord r = activityToReason.keyAt(index);
+            final TransitionInfo info = getActiveTransitionInfo(r);
+            if (info == null || info.mLoggedTransitionStarting) {
+                // Ignore any subsequent notifyTransitionStarting.
                 continue;
             }
-            info.reason = windowingModeToReason.valueAt(index);
-            foundInfo = info;
+            if (DEBUG_METRICS) {
+                Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
+            }
+
+            info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
+            info.mReason = activityToReason.valueAt(index);
+            info.mLoggedTransitionStarting = true;
+            if (info.allDrawn()) {
+                done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
+                        timestampNs);
+            }
         }
-        if (allWindowsDrawn()) {
-            // abort metrics collection if we cannot find a matching transition.
-            final boolean abortMetrics = foundInfo == null;
-            reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
-                timestampNs /* timestampNs */);
-        }
+    }
+
+    /** Makes sure that the reference to the removed activity is cleared. */
+    void notifyActivityRemoved(@NonNull ActivityRecord r) {
+        mLastTransitionInfo.remove(r);
     }
 
     /**
      * Notifies the tracker that the visibility of an app is changing.
      *
-     * @param activityRecord the app that is changing its visibility
+     * @param r the app that is changing its visibility
      */
-    void notifyVisibilityChanged(ActivityRecord activityRecord) {
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                activityRecord.getWindowingMode());
+    void notifyVisibilityChanged(@NonNull ActivityRecord r) {
+        final TransitionInfo info = getActiveTransitionInfo(r);
         if (info == null) {
             return;
         }
-        if (info.launchedActivity != activityRecord) {
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
+                    + " state=" + r.getState() + " finishing=" + r.finishing);
+        }
+        if (!r.mVisibleRequested || r.finishing) {
+            info.removePendingDrawActivity(r);
+        }
+        if (info.mLastLaunchedActivity != r) {
             return;
         }
-        final Task t = activityRecord.getTask();
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = t;
-        args.arg2 = activityRecord;
-        mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
+        // The activity and its task are passed separately because the activity may be removed from
+        // the task later.
+        r.mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
+                ActivityMetricsLogger::checkVisibility, this, r.getTask(), r));
     }
 
     /** @return {@code true} if the given task has an activity will be drawn. */
     private static boolean hasActivityToBeDrawn(Task t) {
-        for (int i = t.getChildCount() - 1; i >= 0; --i) {
-            final ActivityRecord r = t.getChildAt(i);
-            if (r.mVisibleRequested && !r.mDrawn && !r.finishing) {
-                return true;
-            }
-        }
-        return false;
+        return t.forAllActivities((r) -> r.mVisibleRequested && !r.mDrawn && !r.finishing);
     }
 
     private void checkVisibility(Task t, ActivityRecord r) {
         synchronized (mSupervisor.mService.mGlobalLock) {
 
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                    r.getWindowingMode());
+            final TransitionInfo info = getActiveTransitionInfo(r);
 
             // If we have an active transition that's waiting on a certain activity that will be
             // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
@@ -571,7 +677,7 @@
 
             // The notified activity whose visibility changed is no longer the launched activity.
             // We can still wait to get onWindowsDrawn.
-            if (info.launchedActivity != r) {
+            if (info.mLastLaunchedActivity != r) {
                 return;
             }
 
@@ -585,11 +691,7 @@
 
             if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r);
             logAppTransitionCancel(info);
-            // Abort if this is the only one active transition.
-            if (mWindowingModeTransitionInfo.size() == 1
-                    && mWindowingModeTransitionInfo.get(r.getWindowingMode()) != null) {
-                abort(info, "notifyVisibilityChanged to invisible");
-            }
+            abort(info, "notifyVisibilityChanged to invisible");
         }
     }
 
@@ -599,137 +701,86 @@
      * @param appInfo The client into which we'll call bindApplication.
      */
     void notifyBindApplication(ApplicationInfo appInfo) {
-        for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
+        for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+            final TransitionInfo info = mTransitionInfoList.get(i);
 
             // App isn't attached to record yet, so match with info.
-            if (info.launchedActivity.info.applicationInfo == appInfo) {
-                info.bindApplicationDelayMs = calculateCurrentDelay();
+            if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
+                info.mBindApplicationDelayMs = info.calculateCurrentDelay();
             }
         }
     }
 
-    @VisibleForTesting
-    boolean allWindowsDrawn() {
-        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
-            if (mWindowingModeTransitionInfo.valueAt(index).numUndrawnActivities != 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private boolean isAnyTransitionActive() {
-        return mCurrentTransitionStartTimeNs != INVALID_START_TIME
-                && mWindowingModeTransitionInfo.size() > 0;
-    }
-
     /** Aborts tracking of current launch metrics. */
-    private void abort(WindowingModeTransitionInfo info, String cause) {
-        reset(true /* abort */, info, cause, 0L /* timestampNs */);
+    private void abort(TransitionInfo info, String cause) {
+        done(true /* abort */, info, cause, 0L /* timestampNs */);
     }
 
-    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+    /** Called when the given transition (info) is no longer active. */
+    private void done(boolean abort, @Nullable TransitionInfo info, String cause,
             long timestampNs) {
-        final boolean isAnyTransitionActive = isAnyTransitionActive();
         if (DEBUG_METRICS) {
-            Slog.i(TAG, "reset abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
-                    + " active=" + isAnyTransitionActive);
+            Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
+                    + " info=" + info);
         }
-        if (!abort && isAnyTransitionActive) {
-            logAppTransitionMultiEvents();
-        }
-        stopLaunchTrace(info);
-
-        // Ignore reset-after reset.
-        if (isAnyTransitionActive) {
-            // LaunchObserver callbacks.
-            if (abort) {
-                launchObserverNotifyActivityLaunchCancelled(info);
-            } else {
-                launchObserverNotifyActivityLaunchFinished(info, timestampNs);
-            }
-        } else {
+        if (info == null) {
             launchObserverNotifyIntentFailed();
-        }
-
-        mCurrentTransitionStartTimeNs = INVALID_START_TIME;
-        mCurrentTransitionDelayMs = INVALID_DELAY;
-        mLoggedTransitionStarting = false;
-        mWindowingModeTransitionInfo.clear();
-    }
-
-    private int calculateCurrentDelay() {
-        // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) TimeUnit.NANOSECONDS
-            .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
-    }
-
-    private int calculateDelay(long timestampNs) {
-        // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
-            mCurrentTransitionStartTimeNs);
-    }
-
-    private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
-        final int type = getTransitionType(info);
-        if (type == INVALID_TRANSITION_TYPE) {
             return;
         }
+
+        stopLaunchTrace(info);
+        if (abort) {
+            launchObserverNotifyActivityLaunchCancelled(info);
+        } else {
+            logAppTransitionFinished(info);
+            launchObserverNotifyActivityLaunchFinished(info, timestampNs);
+        }
+        info.mPendingDrawActivities.clear();
+        mTransitionInfoList.remove(info);
+    }
+
+    private void logAppTransitionCancel(TransitionInfo info) {
+        final int type = info.mTransitionType;
+        final ActivityRecord activity = info.mLastLaunchedActivity;
         final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
-        builder.setPackageName(info.launchedActivity.packageName);
+        builder.setPackageName(activity.packageName);
         builder.setType(type);
-        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
+        builder.addTaggedData(FIELD_CLASS_NAME, activity.info.name);
         mMetricsLogger.write(builder);
         StatsLog.write(
                 StatsLog.APP_START_CANCELED,
-                info.launchedActivity.info.applicationInfo.uid,
-                info.launchedActivity.packageName,
+                activity.info.applicationInfo.uid,
+                activity.packageName,
                 convertAppStartTransitionType(type),
-                info.launchedActivity.info.name);
+                activity.info.name);
         if (DEBUG_METRICS) {
             Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
-                    info.launchedActivity.info.applicationInfo.uid,
-                    info.launchedActivity.packageName,
+                    activity.info.applicationInfo.uid,
+                    activity.packageName,
                     convertAppStartTransitionType(type),
-                    info.launchedActivity.info.name));
+                    activity.info.name));
         }
     }
 
-    private void logAppTransitionMultiEvents() {
-        if (DEBUG_METRICS) Slog.i(TAG, "logging transition events");
-        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
-            final int type = getTransitionType(info);
-            if (type == INVALID_TRANSITION_TYPE) {
-                if (DEBUG_METRICS) {
-                    Slog.i(TAG, "invalid transition type"
-                            + " processRunning=" + info.currentTransitionProcessRunning
-                            + " startResult=" + info.startResult);
-                }
-                return;
-            }
+    private void logAppTransitionFinished(@NonNull TransitionInfo info) {
+        if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info);
 
-            // Take a snapshot of the transition info before sending it to the handler for logging.
-            // This will avoid any races with other operations that modify the ActivityRecord.
-            final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                     new WindowingModeTransitionInfoSnapshot(info);
-            final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
-            final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
-            BackgroundThread.getHandler().post(() -> logAppTransition(
-                    currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
-            BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
-            if (info.pendingFullyDrawn != null) {
-                info.pendingFullyDrawn.run();
-            }
-
-            info.launchedActivity.info.launchToken = null;
+        // Take a snapshot of the transition info before sending it to the handler for logging.
+        // This will avoid any races with other operations that modify the ActivityRecord.
+        final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
+        BackgroundThread.getHandler().post(() -> logAppTransition(
+                info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs, infoSnapshot));
+        BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
+        if (info.mPendingFullyDrawn != null) {
+            info.mPendingFullyDrawn.run();
         }
+
+        info.mLastLaunchedActivity.info.launchToken = null;
     }
 
     // This gets called on a background thread without holding the activity manager lock.
     private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
-            WindowingModeTransitionInfoSnapshot info) {
+            TransitionInfoSnapshot info) {
         final LogMaker builder = new LogMaker(APP_TRANSITION);
         builder.setPackageName(info.packageName);
         builder.setType(info.type);
@@ -800,7 +851,7 @@
         logAppStartMemoryStateCapture(info);
     }
 
-    private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) {
+    private void logAppDisplayed(TransitionInfoSnapshot info) {
         if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
             return;
         }
@@ -831,26 +882,25 @@
         return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
     }
 
-    /** @return the last known window drawn delay of the given windowing mode. */
-    int getLastDrawnDelayMs(@WindowingMode int windowingMode) {
-        final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
-                windowingMode);
-        return info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+    /** @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;
     }
 
-    WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
+    /** @see android.app.Activity#reportFullyDrawn */
+    TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
             boolean restoredFromBundle) {
-        final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
-                r.getWindowingMode());
+        final TransitionInfo info = mLastTransitionInfo.get(r);
         if (info == null) {
             return null;
         }
-        if (info.numUndrawnActivities > 0 && info.pendingFullyDrawn == null) {
+        if (!info.allDrawn() && info.mPendingFullyDrawn == null) {
             // There are still undrawn activities, postpone reporting fully drawn until all of its
             // windows are drawn. So that is closer to an usable state.
-            info.pendingFullyDrawn = () -> {
+            info.mPendingFullyDrawn = () -> {
                 logAppTransitionReportedDrawn(r, restoredFromBundle);
-                info.pendingFullyDrawn = null;
+                info.mPendingFullyDrawn = null;
             };
             return null;
         }
@@ -859,39 +909,39 @@
         // actually used to trace this function, but instead the logical task that this function
         // fullfils (handling reportFullyDrawn() callbacks).
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
+                "ActivityManager:ReportingFullyDrawn " + info.mLastLaunchedActivity.packageName);
 
         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
         builder.setPackageName(r.packageName);
         builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
         final long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
-        final long startupTimeMs = info.pendingFullyDrawn != null
-                ? info.windowsDrawnDelayMs
-                : TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
+        final long startupTimeMs = info.mPendingFullyDrawn != null
+                ? info.mWindowsDrawnDelayMs
+                : TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - info.mTransitionStartTimeNs);
         builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
         builder.setType(restoredFromBundle
                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
                 : TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
         builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
-                info.currentTransitionProcessRunning ? 1 : 0);
+                info.mProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
         StatsLog.write(
                 StatsLog.APP_START_FULLY_DRAWN,
-                info.launchedActivity.info.applicationInfo.uid,
-                info.launchedActivity.packageName,
+                info.mLastLaunchedActivity.info.applicationInfo.uid,
+                info.mLastLaunchedActivity.packageName,
                 restoredFromBundle
                         ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
                         : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
-                info.launchedActivity.info.name,
-                info.currentTransitionProcessRunning,
+                info.mLastLaunchedActivity.info.name,
+                info.mProcessRunning,
                 startupTimeMs);
 
         // Ends the trace started at the beginning of this function. This is located here to allow
         // the trace slice to have a noticable duration.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-        final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
+        final TransitionInfoSnapshot infoSnapshot =
+                new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
 
         // Notify reportFullyDrawn event.
@@ -900,7 +950,7 @@
         return infoSnapshot;
     }
 
-    private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) {
+    private void logAppFullyDrawn(TransitionInfoSnapshot info) {
         if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
             return;
         }
@@ -976,23 +1026,7 @@
         mMetricsLogger.write(builder);
     }
 
-    private int getTransitionType(WindowingModeTransitionInfo info) {
-        if (info.currentTransitionProcessRunning) {
-            if (info.startResult == START_SUCCESS) {
-                return TYPE_TRANSITION_WARM_LAUNCH;
-            } else if (info.startResult == START_TASK_TO_FRONT) {
-                return TYPE_TRANSITION_HOT_LAUNCH;
-            }
-        } else if (info.startResult == START_SUCCESS
-                || (info.startResult == START_TASK_TO_FRONT)) {
-            // Task may still exist when cold launching an activity and the start
-            // result will be set to START_TASK_TO_FRONT. Treat this as a COLD launch.
-            return TYPE_TRANSITION_COLD_LAUNCH;
-        }
-        return INVALID_TRANSITION_TYPE;
-    }
-
-    private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
+    private void logAppStartMemoryStateCapture(TransitionInfoSnapshot info) {
         if (info.processRecord == null) {
             if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
             return;
@@ -1018,13 +1052,6 @@
                 memoryStat.swapInBytes);
     }
 
-    private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
-        return launchedActivity != null
-                ? mSupervisor.mService.mProcessNames.get(
-                        launchedActivity.processName, launchedActivity.info.applicationInfo.uid)
-                : null;
-    }
-
     private ArtManagerInternal getArtManagerInternal() {
         if (mArtManagerInternal == null) {
             // Note that this may be null.
@@ -1035,30 +1062,24 @@
         return mArtManagerInternal;
     }
 
-    /**
-     * Starts traces for app launch.
-     *
-     * @param info
-     * */
-    private void startTraces(WindowingModeTransitionInfo info) {
-        if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER) || info == null
-                || info.launchTraceActive) {
+    /** Starts trace for an activity is actually launching. */
+    private void startLaunchTrace(@NonNull TransitionInfo info) {
+        if (DEBUG_METRICS) Slog.i(TAG, "startLaunchTrace " + info);
+        if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
             return;
         }
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
-                + info.launchedActivity.packageName, 0);
-        info.launchTraceActive = true;
+        info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName;
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName, 0);
     }
 
-    private void stopLaunchTrace(WindowingModeTransitionInfo info) {
-        if (info == null) {
+    /** Stops trace for the launch is completed or cancelled. */
+    private void stopLaunchTrace(@NonNull TransitionInfo info) {
+        if (DEBUG_METRICS) Slog.i(TAG, "stopLaunchTrace " + info);
+        if (info.mLaunchTraceName == null) {
             return;
         }
-        if (info.launchTraceActive) {
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
-                    + info.launchedActivity.packageName, 0);
-            info.launchTraceActive = false;
-        }
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName, 0);
+        info.mLaunchTraceName = null;
     }
 
     public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
@@ -1094,16 +1115,16 @@
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
      * has started.
      */
-    private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+    private void launchObserverNotifyActivityLaunched(TransitionInfo info) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyActivityLaunched");
 
         @ActivityMetricsLaunchObserver.Temperature int temperature =
-                convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
+                convertTransitionTypeToLaunchObserverTemperature(info.mTransitionType);
 
         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
-        mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
-                                           temperature);
+        mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.mLastLaunchedActivity),
+                temperature);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -1122,12 +1143,12 @@
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
      * cancelled.
      */
-    private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
+    private void launchObserverNotifyActivityLaunchCancelled(TransitionInfo info) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
 
         final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
-                info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+                info != null ? convertActivityRecordToProto(info.mLastLaunchedActivity) : null;
 
         mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
 
@@ -1138,14 +1159,12 @@
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
      * has fully finished (successfully).
      */
-    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
-        long timestampNs) {
+    private void launchObserverNotifyActivityLaunchFinished(TransitionInfo info, long timestampNs) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
 
-        mLaunchObserver
-            .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
-                timestampNs);
+        mLaunchObserver.onActivityLaunchFinished(
+                convertActivityRecordToProto(info.mLastLaunchedActivity), timestampNs);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3de3578..963e090 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -301,6 +301,9 @@
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AttributeCache;
 import com.android.server.LocalServices;
 import com.android.server.am.AppTimeTracker;
@@ -309,7 +312,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 import com.android.server.uri.UriPermissionOwner;
-import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
+import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
 import com.android.server.wm.ActivityStack.ActivityState;
 import com.android.server.wm.WindowManagerService.H;
 import com.android.server.wm.utils.InsetUtils;
@@ -695,12 +698,6 @@
         }
     }
 
-    void dump(PrintWriter pw, String prefix) {
-    }
-
-    /**
-     * Copied from old AppWindowToken.
-     */
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final long now = SystemClock.uptimeMillis();
@@ -940,6 +937,10 @@
         }
     }
 
+    void setAppTimeTracker(AppTimeTracker att) {
+        appTimeTracker = att;
+    }
+
     /** Update the saved state of an activity. */
     void setSavedState(@Nullable Bundle savedState) {
         mIcicle = savedState;
@@ -1167,6 +1168,15 @@
 
         super.onParentChanged(newParent, oldParent);
 
+        if (isPersistable()) {
+            if (oldTask != null) {
+                mAtmService.notifyTaskPersisterLocked(oldTask, false);
+            }
+            if (newTask != null) {
+                mAtmService.notifyTaskPersisterLocked(newTask, false);
+            }
+        }
+
         if (oldParent == null && newParent != null) {
             // First time we are adding the activity to the system.
             mVoiceInteraction = newTask.voiceSession != null;
@@ -1261,12 +1271,8 @@
 
         if (prevDc.mFocusedApp == this) {
             prevDc.setFocusedApp(null);
-            final ActivityStack stack = dc.getTopStack();
-            if (stack != null) {
-                final Task task = stack.getTopChild();
-                if (task != null && task.getTopChild() == this) {
-                    dc.setFocusedApp(this);
-                }
+            if (dc.getTopMostActivity() == this) {
+                dc.setFocusedApp(this);
             }
         }
 
@@ -1520,7 +1526,13 @@
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
 
-        taskAffinity = aInfo.taskAffinity;
+        // b/35954083: Limit task affinity to uid to avoid various issues associated with sharing
+        // affinity across uids.
+        final String uid = Integer.toString(info.applicationInfo.uid);
+        if (info.taskAffinity != null && !info.taskAffinity.startsWith(uid)) {
+            info.taskAffinity = uid + ":" + info.taskAffinity;
+        }
+        taskAffinity = info.taskAffinity;
         stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
         nonLocalizedLabel = aInfo.nonLocalizedLabel;
         labelRes = aInfo.labelRes;
@@ -1798,7 +1810,7 @@
                 // task snapshot starting window.
                 return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
             }
-            return snapshot == null ? STARTING_WINDOW_TYPE_SPLASH_SCREEN
+            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
                     : snapshotOrientationSameAsTask(snapshot) || fromRecents
                             ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else {
@@ -2206,7 +2218,7 @@
                 OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
     }
 
-    boolean isAlwaysFocusable() {
+    private boolean isAlwaysFocusable() {
         return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
     }
 
@@ -2263,23 +2275,28 @@
         return true;
     }
 
+    void finishIfSubActivity(ActivityRecord parent, String otherResultWho, int otherRequestCode) {
+        if (resultTo != parent
+                || requestCode != otherRequestCode
+                || !Objects.equals(resultWho, otherResultWho)) return;
+
+        finishIfPossible("request-sub", false /* oomAdj */);
+    }
+
     /** Finish all activities in the task with the same affinity as this one. */
-    void finishActivityAffinity() {
-        final ArrayList<ActivityRecord> activities = task.mChildren;
-        for (int index = activities.indexOf(this); index >= 0; --index) {
-            final ActivityRecord cur = activities.get(index);
-            if (!Objects.equals(cur.taskAffinity, taskAffinity)) {
-                break;
-            }
-            cur.finishIfPossible("request-affinity", true /* oomAdj */);
-        }
+    boolean finishIfSameAffinity(ActivityRecord r) {
+        // End search once we get to the activity that doesn't have the same affinity.
+        if (!Objects.equals(r.taskAffinity, taskAffinity)) return true;
+
+        r.finishIfPossible("request-affinity", true /* oomAdj */);
+        return false;
     }
 
     /**
      * Sets the result for activity that started this one, clears the references to activities
      * started for result from this one, and clears new intents.
      */
-    void finishActivityResults(int resultCode, Intent resultData) {
+    private void finishActivityResults(int resultCode, Intent resultData) {
         // Send the result if needed
         if (resultTo != null) {
             if (DEBUG_RESULTS) {
@@ -2378,14 +2395,12 @@
             final Task task = getTask();
             EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),
                     task.mTaskId, shortComponentName, reason);
-            final ArrayList<ActivityRecord> activities = task.mChildren;
-            final int index = activities.indexOf(this);
-            if (index < (task.getChildCount() - 1)) {
+            ActivityRecord next = task.getActivityAbove(this);
+            if (next != null) {
                 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
                     // If the caller asked that this activity (and all above it)
                     // be cleared when the task is reset, don't lose that information,
                     // but propagate it up to the next activity.
-                    final ActivityRecord next = task.getChildAt(index + 1);
                     next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                 }
             }
@@ -2403,7 +2418,7 @@
                 } else {
                     // Only move the next stack to top in its display.
                     final ActivityDisplay display = stack.getDisplay();
-                    final ActivityRecord next = display.topRunningActivity();
+                    next = display.topRunningActivity();
                     if (next != null) {
                         display.positionChildAtTop(next.getActivityStack(),
                                 false /* includingParents */, "finish-display-top");
@@ -2413,7 +2428,8 @@
 
             finishActivityResults(resultCode, resultData);
 
-            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
+            final boolean endTask = task.getActivityBelow(this) == null
+                    && !task.isClearingToReuseTask();
             final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
             if (isState(RESUMED)) {
                 if (endTask) {
@@ -2473,16 +2489,13 @@
                 // sync with the activity visibility being set for this finishing activity above.
                 // In this case, we can set the visibility of all the task overlay activities when
                 // we detect the last one is finishing to keep them in sync.
-                if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
-                    for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
-                        final ActivityRecord taskOverlay = task.getChildAt(i);
-                        if (!taskOverlay.mTaskOverlay) {
-                            continue;
-                        }
-                        taskOverlay.prepareActivityHideTransitionAnimation(transit);
-                    }
+                if (task.onlyHasTaskOverlayActivities(false /* includeFinishing */)) {
+                    final PooledConsumer c = PooledLambda.obtainConsumer(
+                            ActivityRecord::prepareActivityHideTransitionAnimationIfOvarlay,
+                            PooledLambda.__(ActivityRecord.class), transit);
+                    task.forAllActivities(c);
+                    c.recycle();
                 }
-
                 return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
             } else {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this);
@@ -2494,6 +2507,12 @@
         }
     }
 
+    private void prepareActivityHideTransitionAnimationIfOvarlay(int transit) {
+        if (mTaskOverlay) {
+            prepareActivityHideTransitionAnimation(transit);
+        }
+    }
+
     private void prepareActivityHideTransitionAnimation(int transit) {
         final DisplayContent dc = getDisplay().mDisplayContent;
         dc.prepareAppTransition(transit, false);
@@ -2991,6 +3010,7 @@
         getDisplayContent().mChangingApps.remove(this);
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
         mWmService.mTaskSnapshotController.onAppRemoved(this);
+        mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         waitingToShow = false;
         if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
@@ -3056,7 +3076,7 @@
         }
 
         // Reset the last saved PiP snap fraction on removal.
-        mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+        mDisplayContent.mPinnedStackControllerLocked.resetReentryBounds(mActivityComponent);
 
         mRemovingFromDisplay = false;
     }
@@ -3265,15 +3285,16 @@
      * immediately finishes after, so we have to transfer T to M.
      */
     void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
-        for (int i = task.mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord fromActivity = task.mChildren.get(i);
-            if (fromActivity == this) {
-                return;
-            }
-            if (!fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token)) {
-                return;
-            }
-        }
+        final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
+                this, PooledLambda.__(ActivityRecord.class));
+        task.forAllActivities(p);
+        p.recycle();
+    }
+
+    private boolean transferStartingWindow(ActivityRecord fromActivity) {
+        if (fromActivity == this) return true;
+
+        return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
     }
 
     void checkKeyguardFlagsChanged() {
@@ -3345,7 +3366,7 @@
         if (!inPinnedWindowingMode() && (mShowWhenLocked || containsShowWhenLockedWindow())) {
             return true;
         } else if (mInheritShownWhenLocked) {
-            final ActivityRecord r = getActivityBelow();
+            final ActivityRecord r = task.getActivityBelow(this);
             return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
                     || r.containsShowWhenLockedWindow());
         } else {
@@ -3370,19 +3391,6 @@
                 true /* topToBottom */);
     }
 
-    /**
-     * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
-     * such activity exists.
-     */
-    @Nullable
-    private ActivityRecord getActivityBelow() {
-        final int pos = task.mChildren.indexOf(this);
-        if (pos == -1) {
-            throw new IllegalStateException("Activity not found in its task");
-        }
-        return pos == 0 ? null : task.getChildAt(pos - 1);
-    }
-
     WindowState getImeTargetBelowWindow(WindowState w) {
         final int index = mChildren.indexOf(w);
         if (index > 0) {
@@ -3426,7 +3434,8 @@
     }
 
     @Override
-    boolean forAllActivities(Function<ActivityRecord, Boolean> callback) {
+    boolean forAllActivities(
+            Function<ActivityRecord, Boolean> callback, boolean traverseTopToBottom) {
         return callback.apply(this);
     }
 
@@ -3603,7 +3612,9 @@
                 clearOptionsLocked(false /* withAbort */);
             } else {
                 // This will clear the options for all the ActivityRecords for this Task.
-                task.clearAllPendingOptions();
+                task.forAllActivities((r) -> {
+                    r.clearOptionsLocked(false /* withAbort */);
+                });
             }
         }
     }
@@ -4319,7 +4330,7 @@
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
         mAppStopped = true;
         // Reset the last saved PiP snap fraction on app stop.
-        mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+        mDisplayContent.mPinnedStackControllerLocked.resetReentryBounds(mActivityComponent);
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
         removeStartingWindow();
@@ -4596,16 +4607,15 @@
         }
 
         // Check if position in task allows to become paused
-        final int positionInTask = task.mChildren.indexOf(this);
-        if (positionInTask == -1) {
+        if (!task.hasChild(this)) {
             throw new IllegalStateException("Activity not found in its task");
         }
-        if (positionInTask == task.getChildCount() - 1) {
+        final ActivityRecord activityAbove = task.getActivityAbove(this);
+        if (activityAbove == null) {
             // It's the topmost activity in the task - should become resumed now
             return true;
         }
         // Check if activity above is finishing now and this one becomes the topmost in task.
-        final ActivityRecord activityAbove = task.getChildAt(positionInTask + 1);
         if (activityAbove.finishing) {
             return true;
         }
@@ -4661,7 +4671,7 @@
         stopped = false;
 
         if (isActivityTypeHome()) {
-            mStackSupervisor.updateHomeProcess(task.getChildAt(0).app);
+            mStackSupervisor.updateHomeProcess(task.getBottomMostActivity().app);
         }
 
         if (nowVisible) {
@@ -4976,7 +4986,7 @@
     }
 
     void reportFullyDrawnLocked(boolean restoredFromBundle) {
-        final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+        final TransitionInfoSnapshot info = mStackSupervisor
             .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
         if (info != null) {
             mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -5008,8 +5018,8 @@
         if (!drawn) {
             return;
         }
-        final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-            .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
+        final TransitionInfoSnapshot info = mStackSupervisor
+                .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
         final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
         final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
         mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -5210,7 +5220,8 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                onStartingWindowDrawn(SystemClock.elapsedRealtimeNanos());
+                // The starting window for this container is drawn.
+                mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
                 startingDisplayed = true;
             }
         }
@@ -5218,14 +5229,6 @@
         return isInterestingAndDrawn;
     }
 
-    /** Called when the starting window for this container is drawn. */
-    private void onStartingWindowDrawn(long timestampNs) {
-        synchronized (mAtmService.mGlobalLock) {
-            mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
-                    getWindowingMode(), timestampNs);
-        }
-    }
-
     /**
      * Called when the key dispatching to a window associated with the app window container
      * timed-out.
@@ -5288,6 +5291,10 @@
                 && mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */));
     }
 
+    boolean canBeTopRunning() {
+        return !finishing && okToShowLocked();
+    }
+
     /**
      * This method will return true if the activity is either visible, is becoming visible, is
      * currently pausing, or is resumed.
@@ -5319,13 +5326,12 @@
 
     static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r == null) {
+        if (r == null || r.getParent() == null) {
             return INVALID_TASK_ID;
         }
         final Task task = r.task;
-        final int activityNdx = task.mChildren.indexOf(r);
-        if (activityNdx < 0
-                || (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) {
+        if (onlyRoot && r.compareTo(task.getRootActivity(
+                false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)) > 0) {
             return INVALID_TASK_ID;
         }
         return task.mTaskId;
@@ -6560,7 +6566,7 @@
             stackBounds = mTmpRect;
             pinnedStack.getBounds(stackBounds);
         }
-        mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(
+        mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds(
                 mActivityComponent, stackBounds);
     }
 
@@ -7216,6 +7222,10 @@
         return info.applicationInfo.uid;
     }
 
+    boolean isUid(int uid) {
+        return info.applicationInfo.uid == uid;
+    }
+
     int getPid() {
         return app != null ? app.getPid() : 0;
     }
@@ -7279,13 +7289,8 @@
         if (task == null) {
             return false;
         }
-        final ActivityRecord rootActivity = task.getRootActivity();
-        if (rootActivity != null) {
-            return this == rootActivity;
-        }
-        // No non-finishing activity found. In this case the bottom-most activity is considered to
-        // be the root.
-        return task.getChildAt(0) == this;
+        final ActivityRecord rootActivity = task.getRootActivity(true);
+        return this == rootActivity;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 6ddbb0d..bb3126b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -76,12 +76,10 @@
 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_PAUSE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
@@ -107,7 +105,6 @@
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.RootActivityContainer.FindTaskResult;
 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
 import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
@@ -143,12 +140,10 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
@@ -162,7 +157,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
-import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -178,7 +172,10 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -189,14 +186,12 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 import java.util.function.Consumer;
 
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget,
-        ConfigurationContainerListener {
+class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -499,7 +494,7 @@
                 case DESTROY_ACTIVITIES_MSG: {
                     ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
                     synchronized (mService.mGlobalLock) {
-                        destroyActivitiesLocked(args.mOwner, args.mReason);
+                        destroyActivities(args.mOwner, args.mReason);
                     }
                 } break;
                 case TRANSLUCENT_TIMEOUT_MSG: {
@@ -514,13 +509,226 @@
     private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
     private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
             new EnsureActivitiesVisibleHelper(this);
+    private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+            new EnsureVisibleActivitiesConfigHelper();
+    private class EnsureVisibleActivitiesConfigHelper {
+        private boolean mUpdateConfig;
+        private boolean mPreserveWindow;
+        private boolean mBehindFullscreen;
 
-    int numActivities() {
-        int count = 0;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            count += getChildAt(taskNdx).getChildCount();
+        void reset(boolean preserveWindow) {
+            mPreserveWindow = preserveWindow;
+            mUpdateConfig = false;
+            mBehindFullscreen = false;
         }
-        return count;
+
+        void process(ActivityRecord start, boolean preserveWindow) {
+            if (start == null || !start.mVisibleRequested) {
+                return;
+            }
+            reset(preserveWindow);
+
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    EnsureVisibleActivitiesConfigHelper::processActivity, this,
+                    PooledLambda.__(ActivityRecord.class));
+            forAllActivities(f, start.getTask(), true /*includeBoundary*/,
+                    true /*traverseTopToBottom*/);
+            f.recycle();
+
+            if (mUpdateConfig) {
+                // Ensure the resumed state of the focus activity if we updated the configuration of
+                // any activity.
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
+            }
+        }
+
+        boolean processActivity(ActivityRecord r) {
+            mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+            mBehindFullscreen |= r.occludesParent();
+            return mBehindFullscreen;
+        }
+    }
+
+    private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
+            new CheckBehindFullscreenActivityHelper();
+    private class CheckBehindFullscreenActivityHelper {
+        private boolean mAboveTop;
+        private boolean mBehindFullscreenActivity;
+        private ActivityRecord mToCheck;
+        private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
+        private boolean mHandlingOccluded;
+
+        private void reset(ActivityRecord toCheck,
+                Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+            mToCheck = toCheck;
+            mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
+            mAboveTop = true;
+            mBehindFullscreenActivity = false;
+
+            if (!shouldBeVisible(null)) {
+                // The stack is not visible, so no activity in it should be displaying a starting
+                // window. Mark all activities below top and behind fullscreen.
+                mAboveTop = false;
+                mBehindFullscreenActivity = true;
+            }
+
+            mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
+        }
+
+        boolean process(ActivityRecord toCheck,
+                Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+            reset(toCheck, handleBehindFullscreenActivity);
+
+            if (!mHandlingOccluded && mBehindFullscreenActivity) {
+                return true;
+            }
+
+            final ActivityRecord topActivity = topRunningActivityLocked();
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    CheckBehindFullscreenActivityHelper::processActivity, this,
+                    PooledLambda.__(ActivityRecord.class), topActivity);
+            forAllActivities(f);
+            f.recycle();
+
+            return mBehindFullscreenActivity;
+        }
+
+        private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
+            if (mAboveTop) {
+                if (r == topActivity) {
+                    if (r == mToCheck) {
+                        // It is the top activity in a visible stack.
+                        mBehindFullscreenActivity = false;
+                        return true;
+                    }
+                    mAboveTop = false;
+                }
+                mBehindFullscreenActivity |= r.occludesParent();
+                return false;
+            }
+
+            if (mHandlingOccluded) {
+                mHandleBehindFullscreenActivity.accept(r);
+            } else if (r == mToCheck) {
+                return true;
+            } else if (mBehindFullscreenActivity) {
+                // It is occluded before {@param toCheck} is found.
+                return true;
+            }
+            mBehindFullscreenActivity |= r.occludesParent();
+            return false;
+        }
+    }
+
+    // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
+    private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
+            new RemoveHistoryRecordsForApp();
+    private class RemoveHistoryRecordsForApp {
+        private boolean mHasVisibleActivities;
+        private boolean mIsProcessRemoved;
+        private WindowProcessController mApp;
+        private ArrayList<ActivityRecord> mToRemove = new ArrayList<>();
+
+        boolean process(WindowProcessController app) {
+            mToRemove.clear();
+            mHasVisibleActivities = false;
+            mApp = app;
+            mIsProcessRemoved = app.isRemoved();
+            if (mIsProcessRemoved) {
+                // The package of the died process should be force-stopped, so make its activities
+                // as finishing to prevent the process from being started again if the next top
+                // (or being visible) activity also resides in the same process.
+                app.makeFinishingForProcessRemoved();
+            }
+
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    RemoveHistoryRecordsForApp::addActivityToRemove, this,
+                    PooledLambda.__(ActivityRecord.class));
+            forAllActivities(c);
+            c.recycle();
+
+            while (!mToRemove.isEmpty()) {
+                processActivity(mToRemove.remove(0));
+            }
+
+            mApp = null;
+            return mHasVisibleActivities;
+        }
+
+        private void addActivityToRemove(ActivityRecord r) {
+            if (r.app == mApp) {
+                mToRemove.add(r);
+            }
+        }
+
+        private void processActivity(ActivityRecord r) {
+            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record " + r + ": app=" + r.app);
+
+            if (r.app != mApp) {
+                return;
+            }
+            if (r.isVisible() || r.mVisibleRequested) {
+                // While an activity launches a new activity, it's possible that the old
+                // activity is already requested to be hidden (mVisibleRequested=false), but
+                // this visibility is not yet committed, so isVisible()=true.
+                mHasVisibleActivities = true;
+            }
+            final boolean remove;
+            if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+                    || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+                    && r.launchCount < 3 && !r.finishing) {
+                // If the process crashed during a resize, always try to relaunch it, unless
+                // it has failed more than twice. Skip activities that's already finishing
+                // cleanly by itself.
+                remove = false;
+            } else if ((!r.hasSavedState() && !r.stateNotNeeded
+                    && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
+                // Don't currently have state for the activity, or
+                // it is finishing -- always remove it.
+                remove = true;
+            } else if (!r.mVisibleRequested && r.launchCount > 2
+                    && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+                // We have launched this activity too many times since it was
+                // able to run, so give up and remove it.
+                // (Note if the activity is visible, we don't remove the record.
+                // We leave the dead window on the screen but the process will
+                // not be restarted unless user explicitly tap on it.)
+                remove = true;
+            } else {
+                // The process may be gone, but the activity lives on!
+                remove = false;
+            }
+            if (remove) {
+                if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
+                        "Removing activity " + r + " from stack "
+                                + ": hasSavedState=" + r.hasSavedState()
+                                + " stateNotNeeded=" + r.stateNotNeeded
+                                + " finishing=" + r.finishing
+                                + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
+                if (!r.finishing || mIsProcessRemoved) {
+                    Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
+                    EventLogTags.writeWmFinishActivity(r.mUserId,
+                        System.identityHashCode(r), r.getTask().mTaskId,
+                            r.shortComponentName, "proc died without state saved");
+                }
+            } else {
+                // We have the current state for this activity, so
+                // it can be restarted later when needed.
+                if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
+                if (DEBUG_APP) Slog.v(TAG_APP,
+                        "Clearing app during removeHistory for activity " + r);
+                r.app = null;
+                // Set nowVisible to previous visible state. If the app was visible while
+                // it died, we leave the dead window on screen so it's basically visible.
+                // This is needed when user later tap on the dead window, we need to stop
+                // other apps when user transfers focus to the restarted activity.
+                r.nowVisible = r.mVisibleRequested;
+            }
+            r.cleanUp(true /* cleanServices */, true /* setState */);
+            if (remove) {
+                r.removeFromHistory("appDied");
+            }
+        }
     }
 
     ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
@@ -941,7 +1149,7 @@
         }
     }
 
-    boolean updateBoundsAllowed(Rect bounds) {
+    private boolean updateBoundsAllowed(Rect bounds) {
         if (!mUpdateBoundsDeferred) {
             return true;
         }
@@ -954,7 +1162,7 @@
         return false;
     }
 
-    boolean updateDisplayedBoundsAllowed(Rect bounds) {
+    private boolean updateDisplayedBoundsAllowed(Rect bounds) {
         if (!mUpdateBoundsDeferred) {
             return true;
         }
@@ -971,47 +1179,29 @@
         return topRunningActivityLocked(false /* focusableOnly */);
     }
 
-    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
-        outActivities.clear();
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            getChildAt(taskNdx).getAllRunningVisibleActivitiesLocked(outActivities);
-        }
-    }
-
     ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            ActivityRecord r = getChildAt(taskNdx).topRunningActivityLocked();
-            if (r != null && (!focusableOnly || r.isFocusable())) {
-                return r;
-            }
+        // Split into 2 to avoid object creation due to variable capture.
+        if (focusableOnly) {
+            return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+        } else {
+            return getActivity(ActivityRecord::canBeTopRunning);
         }
-        return null;
     }
 
-    ActivityRecord topRunningNonOverlayTaskActivity() {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (!r.finishing && !r.mTaskOverlay) {
-                    return r;
-                }
-            }
-        }
-        return null;
+    private ActivityRecord topRunningNonOverlayTaskActivity() {
+        return getActivity((r) -> (r.canBeTopRunning() && !r.mTaskOverlay));
     }
 
     ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
-                    return r;
-                }
-            }
-        }
-        return null;
+        final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isTopRunningNonDelayed
+                , PooledLambda.__(ActivityRecord.class), notTop);
+        final ActivityRecord r = getActivity(p);
+        p.recycle();
+        return r;
+    }
+
+    private static boolean isTopRunningNonDelayed(ActivityRecord r, ActivityRecord notTop) {
+        return !r.delayedResume && r != notTop && r.canBeTopRunning();
     }
 
     /**
@@ -1024,30 +1214,19 @@
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            Task task = getChildAt(taskNdx);
-            if (task.mTaskId == taskId) {
-                continue;
-            }
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                // Note: the taskId check depends on real taskId fields being non-zero
-                if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
-                    return r;
-                }
-            }
-        }
-        return null;
+        final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isTopRunning,
+                PooledLambda.__(ActivityRecord.class), taskId, token);
+        final ActivityRecord r = getActivity(p);
+        p.recycle();
+        return r;
+    }
+
+    private static boolean isTopRunning(ActivityRecord r, int taskId, IBinder notTop) {
+        return r.getTask().mTaskId == taskId && r.appToken != notTop && r.canBeTopRunning();
     }
 
     ActivityRecord getTopNonFinishingActivity() {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final ActivityRecord r = getChildAt(taskNdx).getTopNonFinishingActivity();
-            if (r != null) {
-                return r;
-            }
-        }
-        return null;
+        return getTopActivity(false /*includeFinishing*/, true /*includeOverlays*/);
     }
 
     final Task topTask() {
@@ -1087,35 +1266,6 @@
         return null;
     }
 
-    boolean isInStackLocked(Task task) {
-        return mChildren.contains(task);
-    }
-
-    /** Checks if there are tasks with specific UID in the stack. */
-    boolean isUidPresent(int uid) {
-        for (int j = getChildCount() - 1; j >= 0; --j) {
-            final Task task = getChildAt(j);
-            for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
-                final ActivityRecord r = task.getChildAt(i);
-                if (r.getUid() == uid) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /** Get all UIDs that are present in the stack. */
-    void getPresentUIDs(IntArray presentUIDs) {
-        for (int j = getChildCount() - 1; j >= 0; --j) {
-            final Task task = getChildAt(j);
-            for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
-                final ActivityRecord r = task.getChildAt(i);
-                presentUIDs.add(r.getUid());
-            }
-        }
-    }
-
     /** @return true if the stack can only contain one task */
     boolean isSingleTaskInstance() {
         final ActivityDisplay display = getDisplay();
@@ -1232,138 +1382,6 @@
         return display != null && !display.isRemoved();
     }
 
-    /**
-     * Returns the top activity in any existing task matching the given Intent in the input result.
-     * Returns null if no such task is found.
-     */
-    void findTaskLocked(ActivityRecord target, FindTaskResult result) {
-        Intent intent = target.intent;
-        ActivityInfo info = target.info;
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
-        boolean isDocument = intent != null & intent.isDocument();
-        // If documentData is non-null then it must match the existing task data.
-        Uri documentData = isDocument ? intent.getData() : null;
-
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            if (task.voiceSession != null) {
-                // We never match voice sessions; those always run independently.
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
-                continue;
-            }
-            if (task.mUserId != userId) {
-                // Looking for a different task.
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
-                continue;
-            }
-
-            // Overlays should not be considered as the task's logical top activity.
-            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
-            if (r == null || r.finishing || r.mUserId != userId ||
-                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
-                continue;
-            }
-            if (!r.hasCompatibleActivityType(target)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
-                continue;
-            }
-
-            final Intent taskIntent = task.intent;
-            final Intent affinityIntent = task.affinityIntent;
-            final boolean taskIsDocument;
-            final Uri taskDocumentData;
-            if (taskIntent != null && taskIntent.isDocument()) {
-                taskIsDocument = true;
-                taskDocumentData = taskIntent.getData();
-            } else if (affinityIntent != null && affinityIntent.isDocument()) {
-                taskIsDocument = true;
-                taskDocumentData = affinityIntent.getData();
-            } else {
-                taskIsDocument = false;
-                taskDocumentData = null;
-            }
-
-            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
-                    + (task.realActivity != null ? task.realActivity.flattenToShortString() : "")
-                    + "/aff=" + r.getTask().rootAffinity + " to new cls="
-                    + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
-            // TODO Refactor to remove duplications. Check if logic can be simplified.
-            if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
-                    && Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
-                //dump();
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
-                        "For Intent " + intent + " bringing to top: " + r.intent);
-                result.mRecord = r;
-                result.mIdealMatch = true;
-                break;
-            } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
-                    affinityIntent.getComponent().compareTo(cls) == 0 &&
-                    Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
-                //dump();
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
-                        "For Intent " + intent + " bringing to top: " + r.intent);
-                result.mRecord = r;
-                result.mIdealMatch = true;
-                break;
-            } else if (!isDocument && !taskIsDocument
-                    && result.mRecord == null && task.rootAffinity != null) {
-                if (task.rootAffinity.equals(target.taskAffinity)) {
-                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
-                    // It is possible for multiple tasks to have the same root affinity especially
-                    // if they are in separate stacks. We save off this candidate, but keep looking
-                    // to see if there is a better candidate.
-                    result.mRecord = r;
-                    result.mIdealMatch = false;
-                }
-            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
-        }
-    }
-
-    /**
-     * Returns the first activity (starting from the top of the stack) that
-     * is the same as the given activity.  Returns null if no such activity
-     * is found.
-     */
-    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
-                                      boolean compareIntentFilters) {
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
-
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (!r.okToShowLocked()) {
-                    continue;
-                }
-                if (!r.finishing && r.mUserId == userId) {
-                    if (compareIntentFilters) {
-                        if (r.intent.filterEquals(intent)) {
-                            return r;
-                        }
-                    } else {
-                        if (r.intent.getComponent().equals(cls)) {
-                            return r;
-                        }
-                    }
-                }
-            }
-        }
-
-        return null;
-    }
-
     // TODO: Should each user have there own stacks?
     @Override
     void switchUser(int userId) {
@@ -1401,35 +1419,13 @@
 
     void awakeFromSleepingLocked() {
         // Ensure activities are no longer sleeping.
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                r.setSleeping(false);
-            }
-        }
+        forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
         if (mPausingActivity != null) {
             Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
             activityPausedLocked(mPausingActivity.appToken, true);
         }
     }
 
-    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
-        final String packageName = aInfo.packageName;
-        final int userId = UserHandle.getUserId(aInfo.uid);
-
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord ar = task.getChildAt(activityNdx);
-
-                if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
-                    ar.updateApplicationInfo(aInfo);
-                }
-            }
-        }
-    }
-
     void checkReadyForSleep() {
         if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
             mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -1495,15 +1491,11 @@
 
         // Make sure any paused or stopped but visible activities are now sleeping.
         // This ensures that the activity's onStop() is called.
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
-                    r.setSleeping(true);
-                }
+        forAllActivities((r) -> {
+            if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
+                r.setSleeping(true);
             }
-        }
+        });
     }
 
     private boolean containsActivityFromStack(List<ActivityRecord> rs) {
@@ -1779,31 +1771,32 @@
         if (!isAttached() || mForceHidden) {
             return true;
         }
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
+        final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isOpaqueActivity,
+                PooledLambda.__(ActivityRecord.class), starting);
+        final ActivityRecord opaque = getActivity(p);
+        p.recycle();
+        return opaque == null;
+    }
 
-                if (r.finishing) {
-                    // We don't factor in finishing activities when determining translucency since
-                    // they will be gone soon.
-                    continue;
-                }
-
-                if (!r.visibleIgnoringKeyguard && r != starting) {
-                    // Also ignore invisible activities that are not the currently starting
-                    // activity (about to be visible).
-                    continue;
-                }
-
-                if (r.occludesParent() || r.hasWallpaper) {
-                    // Stack isn't translucent if it has at least one fullscreen activity
-                    // that is visible.
-                    return false;
-                }
-            }
+    private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+        if (r.finishing) {
+            // We don't factor in finishing activities when determining translucency since
+            // they will be gone soon.
+            return false;
         }
-        return true;
+
+        if (!r.visibleIgnoringKeyguard && r != starting) {
+            // Also ignore invisible activities that are not the currently starting
+            // activity (about to be visible).
+            return false;
+        }
+
+        if (r.occludesParent() || r.hasWallpaper) {
+            // Stack isn't translucent if it has at least one fullscreen activity
+            // that is visible.
+            return true;
+        }
+        return false;
     }
 
     boolean isTopStackOnDisplay() {
@@ -1958,20 +1951,6 @@
                 : STACK_VISIBILITY_VISIBLE;
     }
 
-    final int rankTaskLayers(int baseLayer) {
-        int layer = 0;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            ActivityRecord r = task.topRunningActivityLocked();
-            if (r == null || r.finishing || !r.mVisibleRequested) {
-                task.mLayerRank = -1;
-            } else {
-                task.mLayerRank = baseLayer + layer++;
-            }
-        }
-        return layer;
-    }
-
     /**
      * Make sure that all activities that need to be visible in the stack (that is, they
      * currently can be seen by the user) actually are and update their configuration.
@@ -2006,12 +1985,6 @@
         }
     }
 
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            getChildAt(taskNdx).addStartingWindowsForVisibleActivities(taskSwitch);
-        }
-    }
-
     /**
      * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
      */
@@ -2127,18 +2100,6 @@
         mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
     }
 
-    void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if ( r.appTimeTracker != except) {
-                    r.appTimeTracker = null;
-                }
-            }
-        }
-    }
-
     /**
      * Called as activities below the top translucent activity are redrawn. When the last one is
      * redrawn notify the top activity by calling
@@ -2186,50 +2147,8 @@
      */
     boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
             Consumer<ActivityRecord> handleBehindFullscreenActivity) {
-        boolean aboveTop = true;
-        boolean behindFullscreenActivity = false;
-
-        if (!shouldBeVisible(null)) {
-            // The stack is not visible, so no activity in it should be displaying a starting
-            // window. Mark all activities below top and behind fullscreen.
-            aboveTop = false;
-            behindFullscreenActivity = true;
-        }
-
-        final boolean handlingOccluded = toCheck == null && handleBehindFullscreenActivity != null;
-        if (!handlingOccluded && behindFullscreenActivity) {
-            return true;
-        }
-
-        final ActivityRecord topActivity = topRunningActivityLocked();
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (aboveTop) {
-                    if (r == topActivity) {
-                        if (r == toCheck) {
-                            // It is the top activity in a visible stack.
-                            return false;
-                        }
-                        aboveTop = false;
-                    }
-                    behindFullscreenActivity |= r.occludesParent();
-                    continue;
-                }
-
-                if (handlingOccluded) {
-                    handleBehindFullscreenActivity.accept(r);
-                } else if (r == toCheck) {
-                    return behindFullscreenActivity;
-                } else if (behindFullscreenActivity) {
-                    // It is occluded before {@param toCheck} is found.
-                    return true;
-                }
-                behindFullscreenActivity |= r.occludesParent();
-            }
-        }
-        return behindFullscreenActivity;
+        return mCheckBehindFullscreenActivityHelper.process(
+                toCheck, handleBehindFullscreenActivity);
     }
 
     /**
@@ -2787,7 +2706,7 @@
                     }
                     break;
                 } else if (!isOccluded) {
-                    isOccluded = task.forAllActivities(ActivityRecord::occludesParent);
+                    isOccluded = task.getActivity(ActivityRecord::occludesParent) != null;
                 }
             }
         }
@@ -2812,7 +2731,7 @@
 
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
         // is false, because the activity won't be visible.
-        if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) {
+        if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {
             final DisplayContent dc = getDisplay().mDisplayContent;
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
@@ -2915,8 +2834,7 @@
         return true;
     }
 
-    private boolean isTaskSwitch(ActivityRecord r,
-            ActivityRecord topFocusedActivity) {
+    private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
         return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
     }
 
@@ -2996,23 +2914,6 @@
         return stack;
     }
 
-    /** Finish all activities that were started for result from the specified activity. */
-    final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.resultTo == self && r.requestCode == requestCode) {
-                    if ((r.resultWho == null && resultWho == null) ||
-                        (r.resultWho != null && r.resultWho.equals(resultWho))) {
-                        r.finishIfPossible("request-sub", false /* oomAdj */);
-                    }
-                }
-            }
-        }
-        mService.updateOomAdj();
-    }
-
     /**
      * Finish the topmost activity that belongs to the crashed app. We may also finish the activity
      * that requested launch of the crashed one to prevent launch-crash loop.
@@ -3023,103 +2924,84 @@
      */
     final Task finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
         ActivityRecord r = topRunningActivityLocked();
-        Task finishedTask = null;
         if (r == null || r.app != app) {
             return null;
         }
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
-        finishedTask = r.getTask();
-        int taskNdx = mChildren.indexOf(finishedTask);
-        final Task task = finishedTask;
-        int activityNdx = task.mChildren.indexOf(r);
+        Task finishedTask = r.getTask();
         getDisplay().mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         r.finishIfPossible(reason, false /* oomAdj */);
-        finishedTask = task;
-        // Also terminate any activities below it that aren't yet
-        // stopped, to avoid a situation where one will get
-        // re-start our crashing activity once it gets resumed again.
-        --activityNdx;
-        if (activityNdx < 0) {
-            do {
-                --taskNdx;
-                if (taskNdx < 0) {
-                    break;
-                }
-                activityNdx = getChildAt(taskNdx).getChildCount() - 1;
-            } while (activityNdx < 0);
-        }
-        if (activityNdx >= 0) {
-            r = getChildAt(taskNdx).getChildAt(activityNdx);
-            if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
-                if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
+
+        // Also terminate any activities below it that aren't yet stopped, to avoid a situation
+        // where one will get re-start our crashing activity once it gets resumed again.
+        final ActivityRecord activityBelow = getActivityBelow(r);
+        if (activityBelow != null) {
+            if (activityBelow.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
+                if (!activityBelow.isActivityTypeHome()
+                        || mService.mHomeProcess != activityBelow.app) {
                     Slog.w(TAG, "  Force finishing activity "
-                            + r.intent.getComponent().flattenToShortString());
-                    r.finishIfPossible(reason, false /* oomAdj */);
+                            + activityBelow.intent.getComponent().flattenToShortString());
+                    activityBelow.finishIfPossible(reason, false /* oomAdj */);
                 }
             }
         }
+
         return finishedTask;
     }
 
-    final void finishVoiceTask(IVoiceInteractionSession session) {
-        IBinder sessionBinder = session.asBinder();
-        boolean didOne = false;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            Task tr = getChildAt(taskNdx);
-            if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
-                for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                    ActivityRecord r = tr.getChildAt(activityNdx);
-                    if (!r.finishing) {
-                        r.finishIfPossible("finish-voice", false /* oomAdj */);
-                        didOne = true;
-                    }
-                }
-            } else {
-                // Check if any of the activities are using voice
-                for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                    ActivityRecord r = tr.getChildAt(activityNdx);
-                    if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
-                        // Inform of cancellation
-                        r.clearVoiceSessionLocked();
-                        try {
-                            r.app.getThread().scheduleLocalVoiceInteractionStarted(
-                                    r.appToken, null);
-                        } catch (RemoteException re) {
-                            // Ok
-                        }
-                        mService.finishRunningVoiceLocked();
-                        break;
-                    }
-                }
-            }
-        }
+    void finishVoiceTask(IVoiceInteractionSession session) {
+        final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask,
+                PooledLambda.__(Task.class), session.asBinder());
+        forAllTasks(c);
+        c.recycle();
+    }
 
-        if (didOne) {
-            mService.updateOomAdj();
+    private static void finishIfVoiceTask(Task tr, IBinder binder) {
+        if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
+            tr.forAllActivities((r) -> {
+                if (r.finishing) return;
+                r.finishIfPossible("finish-voice", false /* oomAdj */);
+                tr.mAtmService.updateOomAdj();
+            });
+        } else {
+            // Check if any of the activities are using voice
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    ActivityStack::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
+                    binder);
+            tr.forAllActivities(f);
+            f.recycle();
         }
     }
 
+    private static boolean finishIfVoiceActivity(ActivityRecord r, IBinder binder) {
+        if (r.voiceSession == null || r.voiceSession.asBinder() != binder) return false;
+        // Inform of cancellation
+        r.clearVoiceSessionLocked();
+        try {
+            r.app.getThread().scheduleLocalVoiceInteractionStarted(r.appToken, null);
+        } catch (RemoteException re) {
+            // Ok Boomer...
+        }
+        r.mAtmService.finishRunningVoiceLocked();
+        return true;
+    }
+
     /** Finish all activities in the stack without waiting. */
     void finishAllActivitiesImmediately() {
-        boolean noActivitiesInStack = true;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                noActivitiesInStack = false;
-                Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
-                r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
-            }
-        }
-        if (noActivitiesInStack) {
+        if (!hasChild()) {
             removeIfPossible();
+            return;
         }
+        forAllActivities((r) -> {
+            Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
+            r.destroyIfPossible("finishAllActivitiesImmediately");
+        });
     }
 
     /** @return true if the stack behind this one is a standard activity type. */
-    boolean inFrontOfStandardStack() {
+    private boolean inFrontOfStandardStack() {
         final ActivityDisplay display = getDisplay();
         if (display == null) {
             return false;
@@ -3166,28 +3048,25 @@
         return false;
     }
 
-    final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
+    boolean navigateUpTo(ActivityRecord srec, Intent destIntent, int resultCode,
             Intent resultData) {
         final Task task = srec.getTask();
-        final ArrayList<ActivityRecord> activities = task.mChildren;
-        final int start = activities.indexOf(srec);
-        if (!mChildren.contains(task) || (start < 0)) {
+
+        if (!mChildren.contains(task) || !task.hasChild(srec)) {
             return false;
         }
-        int finishTo = start - 1;
-        ActivityRecord parent = finishTo < 0 ? null : task.getChildAt(finishTo);
+
+        ActivityRecord parent = task.getActivityBelow(srec);
         boolean foundParentInTask = false;
         final ComponentName dest = destIntent.getComponent();
-        if (start > 0 && dest != null) {
-            for (int i = finishTo; i >= 0; i--) {
-                ActivityRecord r = task.getChildAt(i);
-                if (r.info.packageName.equals(dest.getPackageName()) &&
-                        r.info.name.equals(dest.getClassName())) {
-                    finishTo = i;
-                    parent = r;
-                    foundParentInTask = true;
-                    break;
-                }
+        if (task.getBottomMostActivity() != srec && dest != null) {
+            final ActivityRecord candidate = task.getActivity((ar) ->
+                    ar.info.packageName.equals(dest.getPackageName()) &&
+                    ar.info.name.equals(dest.getClassName()), srec, false /*includeBoundary*/,
+                    true /*traverseTopToBottom*/);
+            if (candidate != null) {
+                parent = candidate;
+                foundParentInTask = true;
             }
         }
 
@@ -3212,13 +3091,24 @@
             }
         }
         final long origId = Binder.clearCallingIdentity();
-        for (int i = start; i > finishTo; i--) {
-            final ActivityRecord r = activities.get(i);
-            r.finishIfPossible(resultCode, resultData, "navigate-up", true /* oomAdj */);
+
+        final int[] resultCodeHolder = new int[1];
+        resultCodeHolder[0] = resultCode;
+        final Intent[] resultDataHolder = new Intent[1];
+        resultDataHolder[0] = resultData;
+        final ActivityRecord finalParent = parent;
+        task.forAllActivities((ar) -> {
+            if (ar == finalParent) return true;
+
+            ar.finishIfPossible(
+                    resultCodeHolder[0], resultDataHolder[0], "navigate-up", true /* oomAdj */);
             // Only return the supplied result for the first activity finished
-            resultCode = Activity.RESULT_CANCELED;
-            resultData = null;
-        }
+            resultCodeHolder[0] = Activity.RESULT_CANCELED;
+            resultDataHolder[0] = null;
+            return false;
+        }, srec, true, true);
+        resultCode = resultCodeHolder[0];
+        resultData = resultDataHolder[0];
 
         if (parent != null && foundParentInTask) {
             final int parentLaunchMode = parent.info.launchMode;
@@ -3285,7 +3175,6 @@
         }
     }
 
-    /// HANDLER INTERFACE BEGIN
     void removeTimeoutsForActivity(ActivityRecord r) {
         mStackSupervisor.removeTimeoutsForActivityLocked(r);
         removePauseTimeoutForActivity(r);
@@ -3343,86 +3232,33 @@
     }
     /// HANDLER INTERFACE END
 
-    private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
-        boolean lastIsOpaque = false;
-        boolean activityRemoved = false;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.finishing) {
-                    continue;
-                }
-                if (r.occludesParent()) {
-                    lastIsOpaque = true;
-                }
-                if (owner != null && r.app != owner) {
-                    continue;
-                }
-                if (!lastIsOpaque) {
-                    continue;
-                }
-                if (r.isDestroyable()) {
-                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
-                            + " in state " + r.getState()
-                            + " resumed=" + mResumedActivity
-                            + " pausing=" + mPausingActivity + " for reason " + reason);
-                    if (r.destroyImmediately(true /* removeFromTask */, reason)) {
-                        activityRemoved = true;
-                    }
-                }
-            }
-        }
-        if (activityRemoved) {
+    private void destroyActivities(WindowProcessController owner, String reason) {
+        try {
+            mStackSupervisor.beginDeferResume();
+
+            final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::destroyActivity,
+                    PooledLambda.__(ActivityRecord.class), owner, reason);
+            forAllActivities(c);
+            c.recycle();
+        } finally {
+            mStackSupervisor.endDeferResume();
             mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
-    final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<Task> tasks,
-            String reason) {
-        // Iterate over tasks starting at the back (oldest) first.
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
-        int maxTasks = tasks.size() / 4;
-        if (maxTasks < 1) {
-            maxTasks = 1;
-        }
-        int numReleased = 0;
-        for (int taskNdx = 0; taskNdx < getChildCount() && maxTasks > 0; taskNdx++) {
-            final Task task = getChildAt(taskNdx);
-            if (!tasks.contains(task)) {
-                continue;
-            }
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
-            int curNum = 0;
-            for (int actNdx = 0; actNdx < task.getChildCount(); actNdx++) {
-                final ActivityRecord activity = task.getChildAt(actNdx);
-                if (activity.app == app && activity.isDestroyable()) {
-                    if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
-                            + " in state " + activity.getState() + " resumed=" + mResumedActivity
-                            + " pausing=" + mPausingActivity + " for reason " + reason);
-                    activity.destroyImmediately(true /* removeFromApp */, reason);
-                    if (task.getChildAt(actNdx) != activity) {
-                        // Was removed from list, back up so we don't miss the next one.
-                        actNdx--;
-                    }
-                    curNum++;
-                }
-            }
-            if (curNum > 0) {
-                numReleased += curNum;
-                maxTasks--;
-                if (getChildAt(taskNdx) != task) {
-                    // The entire task got removed, back up so we don't miss the next one.
-                    taskNdx--;
-                }
-            }
-        }
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE,
-                "Done releasing: did " + numReleased + " activities");
-        return numReleased;
+    private static void destroyActivity(
+            ActivityRecord r, WindowProcessController owner, String reason) {
+        if (r.finishing || (owner != null && r.app != owner) || !r.isDestroyable()) return;
+
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
+                + " in state " + r.getState()
+                + " resumed=" + r.getStack().mResumedActivity
+                + " pausing=" + r.getStack().mPausingActivity + " for reason " + reason);
+
+        r.destroyImmediately(true /* removeFromTask */, reason);
     }
 
-    private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
+    private void removeHistoryRecordsForApp(ArrayList<ActivityRecord> list,
             WindowProcessController app, String listName) {
         int i = list.size();
         if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
@@ -3439,107 +3275,15 @@
         }
     }
 
-    private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
-        removeHistoryRecordsForAppLocked(mLruActivities, app, "mLruActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+    private boolean removeHistoryRecordsForApp(WindowProcessController app) {
+        removeHistoryRecordsForApp(mLruActivities, app, "mLruActivities");
+        removeHistoryRecordsForApp(mStackSupervisor.mStoppingActivities, app,
                 "mStoppingActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
+        removeHistoryRecordsForApp(mStackSupervisor.mGoingToSleepActivities, app,
                 "mGoingToSleepActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
+        removeHistoryRecordsForApp(mStackSupervisor.mFinishingActivities, app,
                 "mFinishingActivities");
-
-        final boolean isProcessRemoved = app.isRemoved();
-        if (isProcessRemoved) {
-            // The package of the died process should be force-stopped, so make its activities as
-            // finishing to prevent the process from being started again if the next top (or being
-            // visible) activity also resides in the same process.
-            app.makeFinishingForProcessRemoved();
-        }
-
-        boolean hasVisibleActivities = false;
-
-        // Clean out the history list.
-        int i = numActivities();
-        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
-                "Removing app " + app + " from history with " + i + " entries");
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = getChildAt(taskNdx).mChildren;
-            mTmpActivities.clear();
-            mTmpActivities.addAll(activities);
-
-            while (!mTmpActivities.isEmpty()) {
-                final int targetIndex = mTmpActivities.size() - 1;
-                final ActivityRecord r = mTmpActivities.remove(targetIndex);
-                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
-                        "Record #" + targetIndex + " " + r + ": app=" + r.app);
-
-                if (r.app == app) {
-                    if (r.isVisible() || r.mVisibleRequested) {
-                        // While an activity launches a new activity, it's possible that the old
-                        // activity is already requested to be hidden (mVisibleRequested=false), but
-                        // this visibility is not yet committed, so isVisible()=true.
-                        hasVisibleActivities = true;
-                    }
-                    final boolean remove;
-                    if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
-                            || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
-                            && r.launchCount < 3 && !r.finishing) {
-                        // If the process crashed during a resize, always try to relaunch it, unless
-                        // it has failed more than twice. Skip activities that's already finishing
-                        // cleanly by itself.
-                        remove = false;
-                    } else if ((!r.hasSavedState() && !r.stateNotNeeded
-                            && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
-                        // Don't currently have state for the activity, or
-                        // it is finishing -- always remove it.
-                        remove = true;
-                    } else if (!r.mVisibleRequested && r.launchCount > 2
-                            && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
-                        // We have launched this activity too many times since it was
-                        // able to run, so give up and remove it.
-                        // (Note if the activity is visible, we don't remove the record.
-                        // We leave the dead window on the screen but the process will
-                        // not be restarted unless user explicitly tap on it.)
-                        remove = true;
-                    } else {
-                        // The process may be gone, but the activity lives on!
-                        remove = false;
-                    }
-                    if (remove) {
-                        if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
-                                "Removing activity " + r + " from stack at " + i
-                                + ": hasSavedState=" + r.hasSavedState()
-                                + " stateNotNeeded=" + r.stateNotNeeded
-                                + " finishing=" + r.finishing
-                                + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
-                        if (!r.finishing || isProcessRemoved) {
-                            Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
-                            EventLogTags.writeWmFinishActivity(r.mUserId,
-                                    System.identityHashCode(r), r.getTask().mTaskId,
-                                    r.shortComponentName, "proc died without state saved");
-                        }
-                    } else {
-                        // We have the current state for this activity, so
-                        // it can be restarted later when needed.
-                        if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
-                        if (DEBUG_APP) Slog.v(TAG_APP,
-                                "Clearing app during removeHistory for activity " + r);
-                        r.app = null;
-                        // Set nowVisible to previous visible state. If the app was visible while
-                        // it died, we leave the dead window on screen so it's basically visible.
-                        // This is needed when user later tap on the dead window, we need to stop
-                        // other apps when user transfers focus to the restarted activity.
-                        r.nowVisible = r.mVisibleRequested;
-                    }
-                    r.cleanUp(true /* cleanServices */, true /* setState */);
-                    if (remove) {
-                        r.removeFromHistory("appDied");
-                    }
-                }
-            }
-        }
-
-        return hasVisibleActivities;
+        return mRemoveHistoryRecordsForApp.process(app);
     }
 
     private void updateTransitLocked(int transit, ActivityOptions options) {
@@ -3559,8 +3303,7 @@
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
         final ActivityStack topStack = getDisplay().getTopStack();
-        final ActivityRecord topActivity = topStack != null
-                ? topStack.getTopNonFinishingActivity() : null;
+        final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null;
         final int numTasks = getChildCount();
         final int index = mChildren.indexOf(tr);
         if (numTasks == 0 || index < 0)  {
@@ -3575,9 +3318,10 @@
 
         if (timeTracker != null) {
             // The caller wants a time tracker associated with this task.
-            for (int i = tr.getChildCount() - 1; i >= 0; i--) {
-                tr.getChildAt(i).appTimeTracker = timeTracker;
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
+                    PooledLambda.__(ActivityRecord.class), timeTracker);
+            tr.forAllActivities(c);
+            c.recycle();
         }
 
         try {
@@ -3711,38 +3455,8 @@
     /**
      * Ensures all visible activities at or below the input activity have the right configuration.
      */
-    void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
-        if (start == null || !start.mVisibleRequested) {
-            return;
-        }
-
-        final Task startTask = start.getTask();
-        boolean behindFullscreen = false;
-        boolean updatedConfig = false;
-
-        for (int taskIndex = mChildren.indexOf(startTask); taskIndex >= 0; --taskIndex) {
-            final Task task = getChildAt(taskIndex);
-            final ArrayList<ActivityRecord> activities = task.mChildren;
-            int activityIndex = (start.getTask() == task)
-                    ? activities.indexOf(start) : activities.size() - 1;
-            for (; activityIndex >= 0; --activityIndex) {
-                final ActivityRecord r = activities.get(activityIndex);
-                updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
-                        preserveWindow);
-                if (r.occludesParent()) {
-                    behindFullscreen = true;
-                    break;
-                }
-            }
-            if (behindFullscreen) {
-                break;
-            }
-        }
-        if (updatedConfig) {
-            // Ensure the resumed state of the focus activity if we updated the configuration of
-            // any activity.
-            mRootActivityContainer.resumeFocusedStacksTopActivities();
-        }
+    void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+        mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
     }
 
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
@@ -3774,7 +3488,7 @@
             setBounds(bounds);
 
             if (!deferResume) {
-                ensureVisibleActivitiesConfigurationLocked(
+                ensureVisibleActivitiesConfiguration(
                         topRunningActivityLocked(), preserveWindows);
             }
         } finally {
@@ -3818,85 +3532,23 @@
         }
     }
 
-    boolean willActivityBeVisibleLocked(IBinder token) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.appToken == token) {
-                    return true;
-                }
-                if (r.occludesParent() && !r.finishing) {
-                    return false;
-                }
-            }
-        }
+    boolean willActivityBeVisible(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
             return false;
         }
-        if (r.finishing) Slog.e(TAG, "willActivityBeVisibleLocked: Returning false,"
+
+        // See if there is an occluding activity on-top of this one.
+        final ActivityRecord occludingActivity = getActivity((ar) ->
+                ar.occludesParent() && !ar.finishing,
+                r, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+        if (occludingActivity != null) return false;
+
+        if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
                 + " would have returned true for r=" + r);
         return !r.finishing;
     }
 
-    void closeSystemDialogsLocked() {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    r.finishIfPossible("close-sys", true /* oomAdj */);
-                }
-            }
-        }
-    }
-
-    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
-            boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        Task lastTask = null;
-        ComponentName homeActivity = null;
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = getChildAt(taskNdx).mChildren;
-            mTmpActivities.clear();
-            mTmpActivities.addAll(activities);
-
-            while (!mTmpActivities.isEmpty()) {
-                ActivityRecord r = mTmpActivities.remove(0);
-                final boolean sameComponent =
-                        (r.packageName.equals(packageName) && (filterByClasses == null
-                                || filterByClasses.contains(r.mActivityComponent.getClassName())))
-                        || (packageName == null && r.mUserId == userId);
-                if ((userId == UserHandle.USER_ALL || r.mUserId == userId)
-                        && (sameComponent || r.getTask() == lastTask)
-                        && (r.app == null || evenPersistent || !r.app.isPersistent())) {
-                    if (!doit) {
-                        if (r.finishing) {
-                            // If this activity is just finishing, then it is not
-                            // interesting as far as something to stop.
-                            continue;
-                        }
-                        return true;
-                    }
-                    if (r.isActivityTypeHome()) {
-                        if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) {
-                            Slog.i(TAG, "Skip force-stop again " + r);
-                            continue;
-                        } else {
-                            homeActivity = r.mActivityComponent;
-                        }
-                    }
-                    didSomething = true;
-                    Slog.i(TAG, "  Force finishing activity " + r);
-                    lastTask = r.getTask();
-                    r.finishIfPossible("force-stop", true);
-                }
-            }
-        }
-        return didSomething;
-    }
-
     /**
      * @return The set of running tasks through {@param tasksOut} that are available to the caller.
      *         If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
@@ -3948,14 +3600,11 @@
     }
 
     void unhandledBackLocked() {
-        final int top = getChildCount() - 1;
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
-        if (top >= 0) {
-            final Task task = getChildAt(top);
-            int activityTop = task.getChildCount() - 1;
-            if (activityTop >= 0) {
-                task.getChildAt(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
-            }
+        final ActivityRecord topActivity = getTopMostActivity();
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
+                "Performing unhandledBack(): top activity: " + topActivity);
+        if (topActivity != null) {
+            topActivity.finishIfPossible("unhandled-back", true /* oomAdj */);
         }
     }
 
@@ -3975,25 +3624,7 @@
             mLastNoHistoryActivity = null;
         }
 
-        return removeHistoryRecordsForAppLocked(app);
-    }
-
-    void handleAppCrash(WindowProcessController app) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = task.getChildAt(activityNdx);
-                if (r.app == app) {
-                    Slog.w(TAG, "  Force finishing activity "
-                            + r.intent.getComponent().flattenToShortString());
-                    // Force the destroy to skip right to removal.
-                    r.app = null;
-                    getDisplay().mDisplayContent.prepareAppTransition(
-                            TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-                    r.destroyIfPossible("handleAppCrashedLocked");
-                }
-            }
-        }
+        return removeHistoryRecordsForApp(app);
     }
 
     boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
@@ -4004,8 +3635,7 @@
         pw.println("  isSleeping=" + shouldSleepActivities());
         pw.println("  mBounds=" + getRequestedOverrideBounds());
 
-        boolean printed = dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
-                needSep);
+        boolean printed = dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
 
         printed |= dumpHistoryList(fd, pw, mLruActivities, "    ", "Run", false,
                 !dumpAll, false, dumpPackage, true,
@@ -4037,15 +3667,14 @@
         return printed;
     }
 
-    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+    private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
             boolean dumpClient, String dumpPackage, boolean needSep) {
 
         if (!hasChild()) {
             return false;
         }
         final String prefix = "    ";
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
+        forAllTasks((task) -> {
             if (needSep) {
                 pw.println("");
             }
@@ -4056,9 +3685,11 @@
             pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
             pw.println(prefix + "* " + task);
             task.dump(pw, prefix + "  ");
-            dumpHistoryList(fd, pw, getChildAt(taskNdx).mChildren,
-                    prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
-        }
+            final ArrayList<ActivityRecord> activities = new ArrayList<>();
+            forAllActivities((Consumer<ActivityRecord>) activities::add);
+            dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
+                    dumpPackage, false, null, task);
+        });
         return true;
     }
 
@@ -4066,31 +3697,21 @@
         ArrayList<ActivityRecord> activities = new ArrayList<>();
 
         if ("all".equals(name)) {
-            for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-                activities.addAll(getChildAt(taskNdx).mChildren);
-            }
+            forAllActivities((Consumer<ActivityRecord>) activities::add);
         } else if ("top".equals(name)) {
-            final int top = getChildCount() - 1;
-            if (top >= 0) {
-                final Task task = getChildAt(top);
-                int listTop = task.getChildCount() - 1;
-                if (listTop >= 0) {
-                    activities.add(task.getChildAt(listTop));
-                }
+            final ActivityRecord topActivity = getTopMostActivity();
+            if (topActivity != null) {
+                activities.add(topActivity);
             }
         } else {
             ItemMatcher matcher = new ItemMatcher();
             matcher.build(name);
 
-            for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = getChildAt(taskNdx);
-                for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                    final ActivityRecord r1 = task.getChildAt(activityNdx);
-                    if (matcher.match(r1, r1.intent.getComponent())) {
-                        activities.add(r1);
-                    }
+            forAllActivities((r) -> {
+                if (matcher.match(r, r.intent.getComponent())) {
+                    activities.add(r);
                 }
-            }
+            });
         }
 
         return activities;
@@ -4101,22 +3722,24 @@
 
         // All activities that came from the package must be
         // restarted as if there was a config change.
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord a = task.getChildAt(activityNdx);
-                if (a.info.packageName.equals(packageName)) {
-                    a.forceNewConfig = true;
-                    if (starting != null && a == starting && a.mVisibleRequested) {
-                        a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
-                    }
-                }
-            }
-        }
+        PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::restartPackage,
+                PooledLambda.__(ActivityRecord.class), starting, packageName);
+        forAllActivities(c);
+        c.recycle();
 
         return starting;
     }
 
+    private static void restartPackage(
+            ActivityRecord r, ActivityRecord starting, String packageName) {
+        if (r.info.packageName.equals(packageName)) {
+            r.forceNewConfig = true;
+            if (starting != null && r == starting && r.mVisibleRequested) {
+                r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
+            }
+        }
+    }
+
     /**
      * Removes the input task from this stack.
      *
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d088a5e..d98aa6f 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -138,9 +138,11 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.UserState;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -442,8 +444,7 @@
         mInitialized = true;
         setRunningTasks(new RunningTasks());
 
-        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
-                mHandler.getLooper());
+        mActivityMetricsLogger = new ActivityMetricsLogger(this, mHandler.getLooper());
         mKeyguardController = new KeyguardController(mService, this);
 
         mPersisterQueue = new PersisterQueue();
@@ -575,8 +576,7 @@
     }
 
     void stopWaitingForActivityVisible(ActivityRecord r) {
-        stopWaitingForActivityVisible(r,
-                getActivityMetricsLogger().getLastDrawnDelayMs(r.getWindowingMode()));
+        stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
     }
 
     void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
@@ -825,7 +825,7 @@
                         task.mTaskId, r.shortComponentName);
                 if (r.isActivityTypeHome()) {
                     // Home process is the root process of the task.
-                    updateHomeProcess(task.getChildAt(0).app);
+                    updateHomeProcess(task.getBottomMostActivity().app);
                 }
                 mService.getPackageManagerInternalLocked().notifyPackageUse(
                         r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1688,7 +1688,7 @@
                 }
             }
             if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+                stack.ensureVisibleActivitiesConfiguration(r, preserveWindows);
             }
         } finally {
             mAllowDockedStackResize = true;
@@ -2485,18 +2485,23 @@
             return;
         }
 
-        for (int i = task.getChildCount() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.getChildAt(i);
-            if (r.attachedToProcess()) {
-                mMultiWindowModeChangedActivities.add(r);
-            }
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                ActivityStackSupervisor::addToMultiWindowModeChangedList, this,
+                PooledLambda.__(ActivityRecord.class));
+        task.forAllActivities(c);
+        c.recycle();
 
         if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
             mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
         }
     }
 
+    private void addToMultiWindowModeChangedList(ActivityRecord r) {
+        if (r.attachedToProcess()) {
+            mMultiWindowModeChangedActivities.add(r);
+        }
+    }
+
     void scheduleUpdatePictureInPictureModeIfNeeded(Task task, ActivityStack prevStack) {
         final ActivityStack stack = task.getStack();
         if (prevStack == null || prevStack == stack
@@ -2507,17 +2512,13 @@
         scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
     }
 
-    void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetStackBounds) {
-        for (int i = task.getChildCount() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.getChildAt(i);
-            if (r.attachedToProcess()) {
-                mPipModeChangedActivities.add(r);
-                // If we are scheduling pip change, then remove this activity from multi-window
-                // change list as the processing of pip change will make sure multi-window changed
-                // message is processed in the right order relative to pip changed.
-                mMultiWindowModeChangedActivities.remove(r);
-            }
-        }
+    private void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetStackBounds) {
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                ActivityStackSupervisor::addToPipModeChangedList, this,
+                PooledLambda.__(ActivityRecord.class));
+        task.forAllActivities(c);
+        c.recycle();
+
         mPipModeChangedTargetStackBounds = targetStackBounds;
 
         if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
@@ -2525,14 +2526,23 @@
         }
     }
 
+    private void addToPipModeChangedList(ActivityRecord r) {
+        if (!r.attachedToProcess()) return;
+
+        mPipModeChangedActivities.add(r);
+        // If we are scheduling pip change, then remove this activity from multi-window
+        // change list as the processing of pip change will make sure multi-window changed
+        // message is processed in the right order relative to pip changed.
+        mMultiWindowModeChangedActivities.remove(r);
+    }
+
     void updatePictureInPictureMode(Task task, Rect targetStackBounds, boolean forceUpdate) {
         mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
-        for (int i = task.getChildCount() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.getChildAt(i);
-            if (r.attachedToProcess()) {
-                r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
-            }
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                ActivityRecord::updatePictureInPictureMode,
+                PooledLambda.__(ActivityRecord.class), targetStackBounds, forceUpdate);
+        task.forAllActivities(c);
+        c.recycle();
     }
 
     void wakeUp(String reason) {
@@ -2751,7 +2761,8 @@
 
                 mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
                         true /* forceSend */, targetActivity);
-                mActivityMetricsLogger.notifyActivityLaunching(task.intent);
+                final LaunchingState launchingState =
+                        mActivityMetricsLogger.notifyActivityLaunching(task.intent);
                 try {
                     mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
                             task.mTaskId, 0, options, true /* fromRecents */);
@@ -2759,8 +2770,8 @@
                     // the override pending app transition will be applied immediately.
                     targetActivity.applyOptionsLocked();
                 } finally {
-                    mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
-                            targetActivity);
+                    mActivityMetricsLogger.notifyActivityLaunched(launchingState,
+                            START_TASK_TO_FRONT, targetActivity);
                 }
 
                 mService.getActivityStartController().postStartActivityProcessingForLastStarter(
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 8420695..b2fb93d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -156,16 +156,16 @@
         mInTask = inTask;
         mActivityOptions = activityOptions;
 
-        if (interceptSuspendedPackageIfNeeded()) {
-            // Skip the rest of interceptions as the package is suspended by device admin so
-            // no user action can undo this.
-            return true;
-        }
         if (interceptQuietProfileIfNeeded()) {
             // If work profile is turned off, skip the work challenge since the profile can only
             // be unlocked when profile's user is running.
             return true;
         }
+        if (interceptSuspendedPackageIfNeeded()) {
+            // Skip the rest of interceptions as the package is suspended by device admin so
+            // no user action can undo this.
+            return true;
+        }
         if (interceptHarmfulAppIfNeeded()) {
             // If the app has a "harmful app" warning associated with it, we should ask to uninstall
             // before issuing the work challenge.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7960842..8164bf4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -121,6 +121,7 @@
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
@@ -572,15 +573,16 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, Task inTask) {
         try {
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
+            final LaunchingState launchingState = mSupervisor.getActivityMetricsLogger()
+                    .notifyActivityLaunching(r.intent, r.resultTo);
             mLastStartReason = "startResolvedActivity";
             mLastStartActivityTimeMs = System.currentTimeMillis();
             mLastStartActivityRecord = r;
             mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                     voiceInteractor, startFlags, doResume, options, inTask,
                     false /* restrictedBgActivity */);
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
-                    mLastStartActivityRecord);
+            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
+                    mLastStartActivityResult, mLastStartActivityRecord);
         } finally {
             onExecutionComplete();
         }
@@ -598,8 +600,14 @@
                 throw new IllegalArgumentException("File descriptors passed in Intent");
             }
 
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent);
+            final LaunchingState launchingState;
+            synchronized (mService.mGlobalLock) {
+                final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
+                launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(
+                        mRequest.intent, caller);
+            }
 
+            // Do not lock the resolving to avoid potential deadlock.
             if (mRequest.activityInfo == null) {
                 mRequest.resolveActivity(mSupervisor);
             }
@@ -643,7 +651,7 @@
                 // Notify ActivityMetricsLogger that the activity has launched.
                 // ActivityMetricsLogger will then wait for the windows to be drawn and populate
                 // WaitResult.
-                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res,
+                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                         mLastStartActivityRecord);
                 return getExternalResult(mRequest.waitResult == null ? res
                         : waitForResult(res, mLastStartActivityRecord));
@@ -1446,7 +1454,7 @@
 
         // Stack should also be detached from display and be removed if it's empty.
         if (startedActivityStack != null && startedActivityStack.isAttached()
-                && startedActivityStack.numActivities() == 0
+                && !startedActivityStack.hasActivity()
                 && !startedActivityStack.isActivityTypeHome()) {
             startedActivityStack.removeIfPossible();
             startedActivityStack = null;
@@ -1640,7 +1648,7 @@
             return START_CANCELED;
         }
 
-        if (mRestrictedBgActivity && (newTask || !targetTask.containsAppUid(mCallingUid))
+        if (mRestrictedBgActivity && (newTask || !targetTask.isUidPresent(mCallingUid))
                 && handleBackgroundActivityAbort(mStartActivity)) {
             Slog.e(TAG, "Abort background activity starts from " + mCallingUid);
             return START_ABORTED;
@@ -1880,8 +1888,8 @@
             // In this case, we are launching an activity in our own task that may
             // already be running somewhere in the history, and we want to shuffle it to
             // the front of the stack if so.
-            final ActivityRecord act = targetTask.findActivityInHistoryLocked(
-                    mStartActivity);
+            final ActivityRecord act =
+                    targetTask.findActivityInHistory(mStartActivity.mActivityComponent);
             if (act != null) {
                 final Task task = act.getTask();
                 task.moveActivityToFrontLocked(act);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index cce005b..25f6d6f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -32,9 +32,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
-import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.app.IVoiceInteractor;
@@ -154,17 +152,6 @@
             IVoiceInteractor mInteractor);
 
     /**
-     * Callback for window manager to let activity manager know that we are finally starting the
-     * app transition;
-     *
-     * @param reasons A map from windowing mode to a reason integer why the transition was started,
-     *                which must be one of the APP_TRANSITION_* values.
-     * @param timestampNs The time at which the app transition started in
-     *                  {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
-     */
-    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
-
-    /**
      * Callback for window manager to let activity manager know that the app transition was
      * cancelled.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bcd5d36..d097368 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -220,7 +220,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -247,6 +246,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AttributeCache;
 import com.android.server.LocalServices;
@@ -695,6 +696,13 @@
         int caller() default NONE;
     }
 
+    private final Runnable mUpdateOomAdjRunnable = new Runnable() {
+        @Override
+	public void run() {
+            mAmInternal.updateOomAdj();
+        }
+    };
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public ActivityTaskManagerService(Context context) {
         mContext = context;
@@ -1219,25 +1227,23 @@
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
         final WaitResult res = new WaitResult();
-        synchronized (mGlobalLock) {
-            enforceNotIsolatedCaller("startActivityAndWait");
-            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                    userId, "startActivityAndWait");
-            // TODO: Switch to user app stacks here.
-            getActivityStartController().obtainStarter(intent, "startActivityAndWait")
-                    .setCaller(caller)
-                    .setCallingPackage(callingPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setActivityOptions(bOptions)
-                    .setUserId(userId)
-                    .setProfilerInfo(profilerInfo)
-                    .setWaitResult(res)
-                    .execute();
-        }
+        enforceNotIsolatedCaller("startActivityAndWait");
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, "startActivityAndWait");
+        // TODO: Switch to user app stacks here.
+        getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setActivityOptions(bOptions)
+                .setUserId(userId)
+                .setProfilerInfo(profilerInfo)
+                .setWaitResult(res)
+                .execute();
         return res;
     }
 
@@ -1245,24 +1251,22 @@
     public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, Configuration config, Bundle bOptions, int userId) {
-        synchronized (mGlobalLock) {
-            enforceNotIsolatedCaller("startActivityWithConfig");
-            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                    "startActivityWithConfig");
-            // TODO: Switch to user app stacks here.
-            return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
-                    .setCaller(caller)
-                    .setCallingPackage(callingPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setGlobalConfiguration(config)
-                    .setActivityOptions(bOptions)
-                    .setUserId(userId)
-                    .execute();
-        }
+        enforceNotIsolatedCaller("startActivityWithConfig");
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                "startActivityWithConfig");
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setGlobalConfiguration(config)
+                .setActivityOptions(bOptions)
+                .setUserId(userId)
+                .execute();
     }
 
 
@@ -1661,7 +1665,14 @@
                 if (getLockTaskController().activityBlockedFromFinish(r)) {
                     return false;
                 }
-                r.finishActivityAffinity();
+
+                final PooledFunction p = PooledLambda.obtainFunction(
+                        ActivityRecord::finishIfSameAffinity, r,
+                        PooledLambda.__(ActivityRecord.class));
+                r.getTask().forAllActivities(
+                        p, r, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+                p.recycle();
+
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -1994,10 +2005,8 @@
                 if (r == null) {
                     return false;
                 }
-                final Task task = r.getTask();
-                int index = task.mChildren.lastIndexOf(r);
-                if (index > 0) {
-                    ActivityRecord under = task.getChildAt(index - 1);
+                final ActivityRecord under = r.getTask().getActivityBelow(r);
+                if (under != null) {
                     under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
                 }
                 return r.setOccludesParent(false);
@@ -2163,7 +2172,7 @@
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
             if (r != null) {
-                return r.getActivityStack().navigateUpToLocked(
+                return r.getActivityStack().navigateUpTo(
                         r, destIntent, resultCode, resultData);
             }
             return false;
@@ -2548,11 +2557,22 @@
     public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode);
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) return;
+
+                final PooledConsumer c = PooledLambda.obtainConsumer(
+                        ActivityRecord::finishIfSubActivity, PooledLambda.__(ActivityRecord.class),
+                        r, resultWho, requestCode);
+                // TODO: This should probably only loop over the task since you need to be in the
+                // same task to return results.
+                r.getActivityStack().forAllActivities(c);
+                c.recycle();
+
+                updateOomAdj();
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
-            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -2561,7 +2581,7 @@
         synchronized (mGlobalLock) {
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
-                return stack.willActivityBeVisibleLocked(token);
+                return stack.willActivityBeVisible(token);
             }
             return false;
         }
@@ -3332,7 +3352,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 final WindowProcessController app = getProcessController(appInt);
-                mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
+                app.releaseSomeActivities("low-mem");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -5545,7 +5565,8 @@
     }
 
     void updateOomAdj() {
-        mH.post(mAmInternal::updateOomAdj);
+        mH.removeCallbacks(mUpdateOomAdjRunnable);
+        mH.post(mUpdateOomAdjRunnable);
     }
 
     void updateCpuStats() {
@@ -6073,15 +6094,6 @@
         }
 
         @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons,
-                long timestampNs) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
-                        reasons, timestampNs);
-            }
-        }
-
-        @Override
         public void notifySingleTaskDisplayDrawn(int displayId) {
             mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
         }
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 9177d25..0de94d9 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -21,7 +21,7 @@
 import static android.content.Context.NOTIFICATION_SERVICE;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION;
+import static android.provider.Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -29,13 +29,11 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
-
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-
 import android.net.Uri;
 import android.os.Bundle;
 
@@ -137,7 +135,7 @@
     }
 
     private PendingIntent getContentIntent(Context context, String packageName) {
-        final Intent intent = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION,
+        final Intent intent = new Intent(ACTION_MANAGE_APP_OVERLAY_PERMISSION,
                 Uri.fromParts("package", packageName, null));
         intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
         // Calls into activity manager...
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ff1b423..e9ad0d3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -52,11 +52,10 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.os.SystemClock;
 import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
@@ -81,7 +80,7 @@
     private final WallpaperController mWallpaperControllerLocked;
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
 
-    private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
+    private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
 
     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
@@ -208,8 +207,8 @@
 
         mDisplayContent.computeImeTarget(true /* updateImeTarget */);
 
-        mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
-            SystemClock.elapsedRealtimeNanos());
+        mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+                mTempTransitionReasons);
 
         if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
             mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
@@ -442,7 +441,8 @@
         }
     }
 
-    private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps, SparseIntArray outReasons) {
+    private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
+            ArrayMap<ActivityRecord, Integer> outReasons) {
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
                 mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -478,11 +478,10 @@
                 if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
                     return false;
                 }
-                final int windowingMode = activity.getWindowingMode();
                 if (allDrawn) {
-                    outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
+                    outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
                 } else {
-                    outReasons.put(windowingMode,
+                    outReasons.put(activity,
                             activity.mStartingData instanceof SplashScreenStartingData
                                     ? APP_TRANSITION_SPLASH_SCREEN
                                     : APP_TRANSITION_SNAPSHOT);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 283b92c..b046b08 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2482,7 +2482,7 @@
                 && !mDividerControllerLocked.isImeHideRequested();
         final ActivityStack dockedStack = getSplitScreenPrimaryStack();
         final boolean dockVisible = dockedStack != null;
-        final Task topDockedTask = dockVisible ? dockedStack.getTopChild() : null;
+        final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
         final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c76d03c..c7c3f8a 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -52,6 +52,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.IDisplayWindowRotationCallback;
+import android.view.IWindowManager;
 import android.view.Surface;
 import android.view.WindowContainerTransaction;
 
@@ -184,32 +185,11 @@
     private boolean mDefaultFixedToUserRotation;
 
     /**
-     * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other
-     * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and
-     * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}.
-     */
-    static final int FIXED_TO_USER_ROTATION_DEFAULT = 0;
-    /**
-     * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play
-     * a role in deciding display rotation.
-     */
-    static final int FIXED_TO_USER_ROTATION_DISABLED = 1;
-    /**
-     * Only use {@link #mUserRotation} as the display rotation.
-     */
-    static final int FIXED_TO_USER_ROTATION_ENABLED = 2;
-    @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED,
-            FIXED_TO_USER_ROTATION_ENABLED })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface FixedToUserRotation {}
-
-    /**
      * A flag to indicate if the display rotation should be fixed to user specified rotation
      * regardless of all other states (including app requrested orientation). {@code true} the
      * display rotation should be fixed to user specified rotation, {@code false} otherwise.
      */
-    @FixedToUserRotation
-    private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT;
+    private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
 
     private int mDemoHdmiRotation;
     private int mDemoRotation;
@@ -723,8 +703,7 @@
         }
     }
 
-    void restoreSettings(int userRotationMode, int userRotation,
-            @FixedToUserRotation int fixedToUserRotation) {
+    void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
         mFixedToUserRotation = fixedToUserRotation;
 
         // We will retrieve user rotation and user rotation mode from settings for default display.
@@ -746,7 +725,7 @@
         mUserRotation = userRotation;
     }
 
-    void setFixedToUserRotation(@FixedToUserRotation int fixedToUserRotation) {
+    void setFixedToUserRotation(int fixedToUserRotation) {
         if (mFixedToUserRotation == fixedToUserRotation) {
             return;
         }
@@ -808,9 +787,9 @@
 
     boolean isFixedToUserRotation() {
         switch (mFixedToUserRotation) {
-            case FIXED_TO_USER_ROTATION_DISABLED:
+            case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
                 return false;
-            case FIXED_TO_USER_ROTATION_ENABLED:
+            case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
                 return true;
             default:
                 return mDefaultFixedToUserRotation;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index dac8b14..df7c070 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -22,7 +22,6 @@
 
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -38,6 +37,7 @@
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayInfo;
+import android.view.IWindowManager;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -111,8 +111,7 @@
         private boolean mShouldShowWithInsecureKeyguard = false;
         private boolean mShouldShowSystemDecors = false;
         private boolean mShouldShowIme = false;
-        private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation =
-                FIXED_TO_USER_ROTATION_DEFAULT;
+        private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
 
         private Entry(String name) {
             mName = name;
@@ -145,7 +144,7 @@
                     && !mShouldShowWithInsecureKeyguard
                     && !mShouldShowSystemDecors
                     && !mShouldShowIme
-                    && mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT;
+                    && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
         }
     }
 
@@ -242,8 +241,7 @@
         writeSettingsIfNeeded(entry, displayInfo);
     }
 
-    void setFixedToUserRotation(DisplayContent displayContent,
-            @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
+    void setFixedToUserRotation(DisplayContent displayContent, int fixedToUserRotation) {
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final Entry entry = getOrCreateEntry(displayInfo);
         entry.mFixedToUserRotation = fixedToUserRotation;
@@ -610,7 +608,7 @@
                 if (entry.mShouldShowIme) {
                     out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
                 }
-                if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) {
+                if (entry.mFixedToUserRotation != IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT) {
                     out.attribute(null, "fixedToUserRotation",
                             Integer.toString(entry.mFixedToUserRotation));
                 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 3b58eca..7645de5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -196,8 +196,7 @@
             // Some stack visibility might change (e.g. docked stack)
             mRootActivityContainer.resumeFocusedStacksTopActivities();
             mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-            mRootActivityContainer.addStartingWindowsForVisibleActivities(
-                    true /* taskSwitch */);
+            mRootActivityContainer.addStartingWindowsForVisibleActivities();
             mWindowManager.executeAppTransition();
         } finally {
             mService.continueWindowLayout();
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index a5b1fda..0853f1f 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -236,10 +236,10 @@
     /**
      * Saves the current snap fraction for re-entry of the current activity into PiP.
      */
-    void saveReentrySnapFraction(final ComponentName componentName, final Rect stackBounds) {
+    void saveReentryBounds(final ComponentName componentName, final Rect stackBounds) {
         if (mPinnedStackListener == null) return;
         try {
-            mPinnedStackListener.onSaveReentrySnapFraction(componentName, stackBounds);
+            mPinnedStackListener.onSaveReentryBounds(componentName, stackBounds);
         } catch (RemoteException e) {
             Slog.e(TAG_WM, "Error delivering save reentry fraction event.", e);
         }
@@ -248,10 +248,10 @@
     /**
      * Resets the last saved snap fraction so that the default bounds will be returned.
      */
-    void resetReentrySnapFraction(ComponentName componentName) {
+    void resetReentryBounds(ComponentName componentName) {
         if (mPinnedStackListener == null) return;
         try {
-            mPinnedStackListener.onResetReentrySnapFraction(componentName);
+            mPinnedStackListener.onResetReentryBounds(componentName);
         } catch (RemoteException e) {
             Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e);
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 824a3c8..f4c867c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -42,7 +42,10 @@
 import android.util.Slog;
 import android.view.IRecentsAnimationRunner;
 
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
 
 /**
@@ -194,7 +197,8 @@
                     true /* forceSend */, targetActivity);
         }
 
-        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
+        final LaunchingState launchingState =
+                mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
 
         if (mCaller != null) {
             mCaller.setRunningRecentsAnimation(true);
@@ -253,8 +257,8 @@
             // we fetch the visible tasks to be controlled by the animation
             mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
 
-            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
-                    targetActivity);
+            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
+                    START_TASK_TO_FRONT, targetActivity);
 
             // Register for stack order changes
             mDefaultDisplay.registerStackOrderChangedListener(this);
@@ -490,13 +494,15 @@
             return null;
         }
 
-        for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
-            final Task task = targetStack.getChildAt(i);
-            if (task.mUserId == mUserId
-                    && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) {
-                return task.getTopNonFinishingActivity();
-            }
-        }
-        return null;
+        final PooledPredicate p = PooledLambda.obtainPredicate(RecentsAnimation::matchesTarget,
+                this, PooledLambda.__(Task.class));
+        final Task task = targetStack.getTask(p);
+        p.recycle();
+        return task != null ? task.getTopNonFinishingActivity() : null;
+    }
+
+    private boolean matchesTarget(Task task) {
+        return task.mUserId == mUserId
+                && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent());
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 282144e..39d08a2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
@@ -42,10 +41,10 @@
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
@@ -55,6 +54,9 @@
 import android.view.SurfaceControl.Transaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.protolog.common.ProtoLog;
@@ -369,13 +371,13 @@
         final ActivityStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
                 targetActivityType);
         if (targetStack != null) {
-            for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
-                final Task t = targetStack.getChildAt(i);
-                if (!visibleTasks.contains(t)) {
-                    visibleTasks.add(t);
-                }
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
+	            { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
+                    visibleTasks);
+            targetStack.forAllTasks(c);
+            c.recycle();
         }
+
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
@@ -499,10 +501,13 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to start recents animation", e);
         }
-        final SparseIntArray reasons = new SparseIntArray();
-        reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
-        mService.mAtmInternal
-            .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
+
+        if (mTargetActivityRecord != null) {
+            final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
+            reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
+            mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
+                    .notifyTransitionStarting(reasons);
+        }
     }
 
     private RemoteAnimationTarget[] createAppAnimations() {
@@ -800,12 +805,12 @@
     private boolean isAnimatingApp(ActivityRecord activity) {
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final Task task = mPendingAnimations.get(i).mTask;
-            for (int j = task.getChildCount() - 1; j >= 0; j--) {
-                final ActivityRecord app = task.getChildAt(j);
-                if (app == activity) {
-                    return true;
-                }
-            }
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    (a, b) -> a == b, activity,
+                    PooledLambda.__(ActivityRecord.class));
+            boolean isAnimatingApp = task.forAllActivities(f);
+            f.recycle();
+            return isAnimatingApp;
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 413dfd5..3aa91d5 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -125,8 +125,7 @@
                     // TODO: We should probably look for other stacks also, since corresponding
                     //  task with the same affinity is unlikely to be in the same stack.
                     final Task targetTask;
-                    final ActivityRecord bottom = mParent.getActivity(
-                            (ar) -> true, false /*traverseTopToBottom*/);
+                    final ActivityRecord bottom = mParent.getBottomMostActivity();
 
                     if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
                         // If the activity currently at the bottom has the same task affinity as
@@ -210,10 +209,8 @@
                 // we have put it on top of another instance of the same activity? Then we drop
                 // the instance below so it remains singleTop.
                 if (r.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
-                    final ArrayList<ActivityRecord> taskActivities = mTargetTask.mChildren;
-                    final int targetNdx = taskActivities.indexOf(r);
-                    if (targetNdx > 0) {
-                        final ActivityRecord p = taskActivities.get(targetNdx - 1);
+                    final ActivityRecord p = mTargetTask.getActivityBelow(r);
+                    if (p != null) {
                         if (p.intent.getComponent().equals(r.intent.getComponent())) {
                             p.finishIfPossible("replace", false /* oomAdj */);
                         }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 8f1d6ee..5fd59fa 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -33,6 +33,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 
 import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
@@ -86,6 +87,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
 import android.os.FactoryTest;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -108,6 +110,11 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
+import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.AppTimeTracker;
@@ -121,6 +128,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -201,14 +209,46 @@
 
     // Whether tasks have moved and we need to rank the tasks before next OOM scoring
     private boolean mTaskLayersChanged = true;
+    private int mTmpTaskLayerRank;
 
-    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+    private boolean mTmpBoolean;
+    private RemoteException mTmpRemoteException;
 
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-    static class FindTaskResult {
+    static class FindTaskResult implements ToBooleanFunction<Task> {
         ActivityRecord mRecord;
         boolean mIdealMatch;
 
+        private ActivityRecord mTarget;
+        private Intent intent;
+        private ActivityInfo info;
+        private ComponentName cls;
+        private int userId;
+        private boolean isDocument;
+        private Uri documentData;
+
+        /**
+         * Returns the top activity in any existing task matching the given Intent in the input
+         * result. Returns null if no such task is found.
+         */
+        void process(ActivityRecord target, ActivityStack parent) {
+            mTarget = target;
+
+            intent = target.intent;
+            info = target.info;
+            cls = intent.getComponent();
+            if (info.targetActivity != null) {
+                cls = new ComponentName(info.packageName, info.targetActivity);
+            }
+            userId = UserHandle.getUserId(info.applicationInfo.uid);
+            isDocument = intent != null & intent.isDocument();
+            // If documentData is non-null then it must match the existing task data.
+            documentData = isDocument ? intent.getData() : null;
+
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + parent);
+            parent.forAllTasks(this);
+        }
+
         void clear() {
             mRecord = null;
             mIdealMatch = false;
@@ -218,6 +258,84 @@
             mRecord = result.mRecord;
             mIdealMatch = result.mIdealMatch;
         }
+
+        @Override
+        public boolean apply(Task task) {
+            if (task.voiceSession != null) {
+                // We never match voice sessions; those always run independently.
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
+                return false;
+            }
+            if (task.mUserId != userId) {
+                // Looking for a different task.
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
+                return false;
+            }
+
+            // Overlays should not be considered as the task's logical top activity.
+            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+            if (r == null || r.finishing || r.mUserId != userId ||
+                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
+                return false;
+            }
+            if (!r.hasCompatibleActivityType(mTarget)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
+                return false;
+            }
+
+            final Intent taskIntent = task.intent;
+            final Intent affinityIntent = task.affinityIntent;
+            final boolean taskIsDocument;
+            final Uri taskDocumentData;
+            if (taskIntent != null && taskIntent.isDocument()) {
+                taskIsDocument = true;
+                taskDocumentData = taskIntent.getData();
+            } else if (affinityIntent != null && affinityIntent.isDocument()) {
+                taskIsDocument = true;
+                taskDocumentData = affinityIntent.getData();
+            } else {
+                taskIsDocument = false;
+                taskDocumentData = null;
+            }
+
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+                    + (task.realActivity != null ? task.realActivity.flattenToShortString() : "")
+                    + "/aff=" + r.getTask().rootAffinity + " to new cls="
+                    + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
+            // TODO Refactor to remove duplications. Check if logic can be simplified.
+            if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
+                    && Objects.equals(documentData, taskDocumentData)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+                //dump();
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
+                mRecord = r;
+                mIdealMatch = true;
+                return true;
+            } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
+                    affinityIntent.getComponent().compareTo(cls) == 0 &&
+                    Objects.equals(documentData, taskDocumentData)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
+                mRecord = r;
+                mIdealMatch = true;
+                return true;
+            } else if (!isDocument && !taskIsDocument
+                    && mRecord == null && task.rootAffinity != null) {
+                if (task.rootAffinity.equals(mTarget.taskAffinity)) {
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
+                    // It is possible for multiple tasks to have the same root affinity especially
+                    // if they are in separate stacks. We save off this candidate, but keep looking
+                    // to see if there is a better candidate.
+                    mRecord = r;
+                    mIdealMatch = false;
+                }
+            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
+
+            return false;
+        }
     }
 
     RootActivityContainer(ActivityTaskManagerService service) {
@@ -772,27 +890,21 @@
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
             final ActivityStack stack = display.getFocusedStack();
-            if (stack != null) {
-                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
-                final ActivityRecord top = stack.topRunningActivityLocked();
-                final int size = mTmpActivityList.size();
-                for (int i = 0; i < size; i++) {
-                    final ActivityRecord activity = mTmpActivityList.get(i);
-                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
-                            && processName.equals(activity.processName)) {
-                        try {
-                            if (mStackSupervisor.realStartActivityLocked(activity, app,
-                                    top == activity /* andResume */, true /* checkConfig */)) {
-                                didSomething = true;
-                            }
-                        } catch (RemoteException e) {
-                            Slog.w(TAG, "Exception in new application when starting activity "
-                                    + top.intent.getComponent().flattenToShortString(), e);
-                            throw e;
-                        }
-                    }
-                }
+            if (stack == null) {
+                continue;
             }
+
+            mTmpRemoteException = null;
+            mTmpBoolean = false; // Set to true if an activity was started.
+            final PooledFunction c = PooledLambda.obtainFunction(
+                    RootActivityContainer::startActivityForAttachedApplicationIfNeeded, this,
+                    PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivityLocked());
+            stack.forAllActivities(c);
+            c.recycle();
+            if (mTmpRemoteException != null) {
+                throw mTmpRemoteException;
+            }
+            didSomething |= mTmpBoolean;
         }
         if (!didSomething) {
             ensureActivitiesVisible(null, 0, false /* preserve_windows */);
@@ -800,6 +912,27 @@
         return didSomething;
     }
 
+    private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
+            WindowProcessController app, ActivityRecord top) {
+        if (r.finishing || !r.okToShowLocked() || !r.visibleIgnoringKeyguard || r.app != null
+                || app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
+            return false;
+        }
+
+        try {
+            if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/,
+                    true /*checkConfig*/)) {
+                mTmpBoolean = true;
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Exception in new application when starting activity "
+                    + top.intent.getComponent().flattenToShortString(), e);
+            mTmpRemoteException = e;
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Make sure that all activities that need to be visible in the system actually are and update
      * their configuration.
@@ -1526,14 +1659,12 @@
         }
     }
 
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.addStartingWindowsForVisibleActivities(taskSwitch);
+    void addStartingWindowsForVisibleActivities() {
+        mRootWindowContainer.forAllActivities((r) -> {
+            if (r.mVisibleRequested) {
+                r.showStartingWindow(null /* prev */, false /* newTask */, true /*taskSwitch*/);
             }
-        }
+        });
     }
 
     void invalidateTaskLayers() {
@@ -1541,27 +1672,37 @@
     }
 
     void rankTaskLayersIfNeeded() {
-        if (!mTaskLayersChanged) {
+        if (!mTaskLayersChanged || mRootWindowContainer == null) {
             return;
         }
         mTaskLayersChanged = false;
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            int baseLayer = 0;
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                baseLayer += stack.rankTaskLayers(baseLayer);
-            }
+        mTmpTaskLayerRank = 0;
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                RootActivityContainer::rankTaskLayerForActivity, this,
+                PooledLambda.__(ActivityRecord.class));
+        mRootWindowContainer.forAllActivities(c);
+        c.recycle();
+    }
+
+    private void rankTaskLayerForActivity(ActivityRecord r) {
+        if (r.canBeTopRunning() && r.mVisibleRequested) {
+            r.getTask().mLayerRank = ++mTmpTaskLayerRank;
+        } else {
+            r.getTask().mLayerRank = -1;
         }
     }
 
     void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.clearOtherAppTimeTrackers(except);
-            }
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                RootActivityContainer::clearOtherAppTimeTrackers,
+                PooledLambda.__(ActivityRecord.class), except);
+        mRootWindowContainer.forAllActivities(c);
+        c.recycle();
+    }
+
+    private static void clearOtherAppTimeTrackers(ActivityRecord r, AppTimeTracker except) {
+        if ( r.appTimeTracker != except) {
+            r.appTimeTracker = null;
         }
     }
 
@@ -1575,30 +1716,6 @@
         }
     }
 
-    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<Task> tasks = app.getReleaseSomeActivitiesTasks();
-        if (tasks == null) {
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
-            return;
-        }
-        // If we have activities in multiple tasks that are in a position to be destroyed,
-        // let's iterate through the tasks and release the oldest one.
-        final int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int stackCount = display.getChildCount();
-            // Step through all stacks starting from behind, to hit the oldest things first.
-            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                // Try to release activities in this stack; if we manage to, we are done.
-                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
-                    return;
-                }
-            }
-        }
-    }
-
     // Tries to put all activity stacks to sleep. Returns true if all stacks were
     // successfully put to sleep.
     boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
@@ -1625,28 +1742,52 @@
     }
 
     void handleAppCrash(WindowProcessController app) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.handleAppCrash(app);
-            }
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                RootActivityContainer::handleAppCrash, PooledLambda.__(ActivityRecord.class), app);
+        mRootWindowContainer.forAllActivities(c);
+        c.recycle();
+    }
+
+    private static void handleAppCrash(ActivityRecord r, WindowProcessController app) {
+        if (r.app != app) return;
+        Slog.w(TAG, "  Force finishing activity "
+                + r.intent.getComponent().flattenToShortString());
+        // Force the destroy to skip right to removal.
+        r.app = null;
+        r.getDisplay().mDisplayContent.prepareAppTransition(
+                TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+        r.destroyIfPossible("handleAppCrashed");
     }
 
     ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord ar = stack.findActivityLocked(
-                        intent, info, compareIntentFilters);
-                if (ar != null) {
-                    return ar;
-                }
+        ComponentName cls = intent.getComponent();
+        if (info.targetActivity != null) {
+            cls = new ComponentName(info.packageName, info.targetActivity);
+        }
+        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
+
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                RootActivityContainer::matchesActivity, PooledLambda.__(ActivityRecord.class),
+                userId, compareIntentFilters, intent, cls);
+        final ActivityRecord r = mRootWindowContainer.getActivity(p);
+        p.recycle();
+        return r;
+    }
+
+    private static boolean matchesActivity(ActivityRecord r, int userId,
+            boolean compareIntentFilters, Intent intent, ComponentName cls) {
+        if (!r.canBeTopRunning() || r.mUserId != userId)  return false;
+
+        if (compareIntentFilters) {
+            if (r.intent.filterEquals(intent)) {
+                return true;
+            }
+        } else {
+            if (r.intent.getComponent().equals(cls)) {
+                return true;
             }
         }
-        return null;
+        return false;
     }
 
     boolean hasAwakeDisplay() {
@@ -1978,39 +2119,104 @@
     }
 
     void closeSystemDialogs() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.closeSystemDialogsLocked();
+        mRootWindowContainer.forAllActivities((r) -> {
+            if ((r.info.flags & ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+                r.finishIfPossible("close-sys", true /* oomAdj */);
             }
+        });
+    }
+
+    FinishDisabledPackageActivitiesHelper mFinishDisabledPackageActivitiesHelper =
+            new FinishDisabledPackageActivitiesHelper();
+    class FinishDisabledPackageActivitiesHelper {
+        private boolean mDidSomething;
+        private String mPackageName;
+        private Set<String> mFilterByClasses;
+        private boolean mDoit;
+        private boolean mEvenPersistent;
+        private int mUserId;
+        private Task mLastTask;
+        private ComponentName mHomeActivity;
+
+        private void reset(String packageName, Set<String> filterByClasses,
+                boolean doit, boolean evenPersistent, int userId) {
+            mDidSomething = false;
+            mPackageName = packageName;
+            mFilterByClasses = filterByClasses;
+            mDoit = doit;
+            mEvenPersistent = evenPersistent;
+            mUserId = userId;
+            mLastTask = null;
+            mHomeActivity = null;
+        }
+
+        boolean process(String packageName, Set<String> filterByClasses,
+                boolean doit, boolean evenPersistent, int userId) {
+            reset(packageName, filterByClasses, doit, evenPersistent, userId);
+
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    FinishDisabledPackageActivitiesHelper::processActivity, this,
+                    PooledLambda.__(ActivityRecord.class));
+            mRootWindowContainer.forAllActivities(f);
+            f.recycle();
+            return mDidSomething;
+        }
+
+        private boolean processActivity(ActivityRecord r) {
+            final boolean sameComponent =
+                    (r.packageName.equals(mPackageName) && (mFilterByClasses == null
+                            || mFilterByClasses.contains(r.mActivityComponent.getClassName())))
+                            || (mPackageName == null && r.mUserId == mUserId);
+            if ((mUserId == UserHandle.USER_ALL || r.mUserId == mUserId)
+                    && (sameComponent || r.getTask() == mLastTask)
+                    && (r.app == null || mEvenPersistent || !r.app.isPersistent())) {
+                if (!mDoit) {
+                    if (r.finishing) {
+                        // If this activity is just finishing, then it is not
+                        // interesting as far as something to stop.
+                        return false;
+                    }
+                    return true;
+                }
+                if (r.isActivityTypeHome()) {
+                    if (mHomeActivity != null && mHomeActivity.equals(r.mActivityComponent)) {
+                        Slog.i(TAG, "Skip force-stop again " + r);
+                        return false;
+                    } else {
+                        mHomeActivity = r.mActivityComponent;
+                    }
+                }
+                mDidSomething = true;
+                Slog.i(TAG, "  Force finishing activity " + r);
+                mLastTask = r.getTask();
+                r.finishIfPossible("force-stop", true);
+            }
+
+            return false;
         }
     }
 
     /** @return true if some activity was finished (or would have finished if doit were true). */
     boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
             boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (stack.finishDisabledPackageActivitiesLocked(
-                        packageName, filterByClasses, doit, evenPersistent, userId)) {
-                    didSomething = true;
-                }
-            }
-        }
-        return didSomething;
+        return mFinishDisabledPackageActivitiesHelper.process(packageName, filterByClasses, doit,
+                evenPersistent, userId);
     }
 
     void updateActivityApplicationInfo(ApplicationInfo aInfo) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.updateActivityApplicationInfoLocked(aInfo);
-            }
+        final String packageName = aInfo.packageName;
+        final int userId = UserHandle.getUserId(aInfo.uid);
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                RootActivityContainer::updateActivityApplicationInfo,
+                PooledLambda.__(ActivityRecord.class), aInfo, userId, packageName);
+        mRootWindowContainer.forAllActivities(c);
+        c.recycle();
+    }
+
+    private static void updateActivityApplicationInfo(
+            ActivityRecord r, ApplicationInfo aInfo, int userId, String packageName) {
+        if (r.mUserId == userId && packageName.equals(r.packageName)) {
+            r.updateApplicationInfo(aInfo);
         }
     }
 
@@ -2063,7 +2269,7 @@
             // If the focused stack is not null or not empty, there should have some activities
             // resuming or resumed. Make sure these activities are idle.
             final ActivityStack stack = display.getFocusedStack();
-            if (stack == null || stack.numActivities() == 0) {
+            if (stack == null || !stack.hasActivity()) {
                 continue;
             }
             final ActivityRecord resumedActivity = stack.getResumedActivity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 619b71c..88a38e0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,7 +67,6 @@
 import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
 import static com.android.server.am.TaskRecordProto.STACK_ID;
 import static com.android.server.am.TaskRecordProto.TASK;
-import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -138,6 +137,10 @@
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.ActivityStack.ActivityState;
 
@@ -152,8 +155,9 @@
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
-class Task extends WindowContainer<ActivityRecord> implements ConfigurationContainerListener {
+class Task extends WindowContainer<WindowContainer> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -382,6 +386,44 @@
     /** @see #setCanAffectSystemUiFlags */
     private boolean mCanAffectSystemUiFlags = true;
 
+    private static Exception sTmpException;
+
+    private final FindRootHelper mFindRootHelper = new FindRootHelper();
+    private class FindRootHelper {
+        private ActivityRecord mRoot;
+
+        private void clear() {
+            mRoot = null;
+        }
+
+        ActivityRecord findRoot(boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
+            final PooledFunction f = PooledLambda.obtainFunction(FindRootHelper::processActivity,
+                    this, PooledLambda.__(ActivityRecord.class), ignoreRelinquishIdentity,
+                    setToBottomIfNone);
+            clear();
+            forAllActivities(f, false /*traverseTopToBottom*/);
+            f.recycle();
+            return mRoot;
+        }
+
+        private boolean processActivity(ActivityRecord r,
+                boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
+            if (mRoot == null && setToBottomIfNone) {
+                // This is the first activity we are process. Set it as the candidate root in case
+                // we don't find a better one.
+                mRoot = r;
+            }
+
+            if (r.finishing) return false;
+
+            // Set this as the candidate root since it isn't finishing.
+            mRoot = r;
+
+            // Only end search if we are ignore relinquishing identity or we are not relinquishing.
+            return ignoreRelinquishIdentity || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+        }
+    }
+
     /**
      * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
      * ActivityInfo, Intent, TaskDescription)} instead.
@@ -464,7 +506,7 @@
         mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
     }
 
-    void cleanUpResourcesForDestroy() {
+    private void cleanUpResourcesForDestroy() {
         if (hasChild()) {
             return;
         }
@@ -931,10 +973,11 @@
         super.onParentChanged(newParent, oldParent);
 
         if (oldStack != null) {
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityRecord activity = getChildAt(i);
-                oldStack.onActivityRemovedFromStack(activity);
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    ActivityStack::onActivityRemovedFromStack, oldStack,
+                    PooledLambda.__(ActivityRecord.class));
+            forAllActivities(c);
+            c.recycle();
 
             if (oldStack.inPinnedWindowingMode()
                     && (newStack == null || !newStack.inPinnedWindowingMode())) {
@@ -945,10 +988,11 @@
         }
 
         if (newStack != null) {
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityRecord activity = getChildAt(i);
-                newStack.onActivityAddedToStack(activity);
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    ActivityStack::onActivityAddedToStack, newStack,
+                    PooledLambda.__(ActivityRecord.class));
+            forAllActivities(c);
+            c.recycle();
 
             // TODO: Ensure that this is actually necessary here
             // Notify the voice session if required
@@ -1068,25 +1112,11 @@
     }
 
     ActivityRecord getRootActivity(boolean setToBottomIfNone) {
-        return getRootActivity(false /*ignoreRelinquishIdentity*/, false /*setToBottomIfNone*/);
+        return getRootActivity(false /*ignoreRelinquishIdentity*/, setToBottomIfNone);
     }
 
     ActivityRecord getRootActivity(boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
-        ActivityRecord root;
-        if (ignoreRelinquishIdentity) {
-            root = getActivity((r) -> !r.finishing, false /*traverseTopToBottom*/);
-        } else {
-            root = getActivity((r) ->
-                            !r.finishing && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0,
-                    false /*traverseTopToBottom*/);
-        }
-
-        if (root == null && setToBottomIfNone) {
-            // All activities in the task are either finishing or relinquish task identity.
-            // But we still want to update the intent, so let's use the bottom activity.
-            root = getActivity((r) -> true, false /*traverseTopToBottom*/);
-        }
-        return root;
+        return mFindRootHelper.findRoot(ignoreRelinquishIdentity, setToBottomIfNone);
     }
 
     ActivityRecord getTopNonFinishingActivity() {
@@ -1094,101 +1124,42 @@
     }
 
     ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityRecord r = getChildAt(i);
-            if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
-                continue;
-            }
-            return r;
-        }
-        return null;
+        return getTopActivity(false /*includeFinishing*/, includeOverlays);
     }
 
     ActivityRecord topRunningActivityLocked() {
-        if (mStack != null) {
-            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = getChildAt(activityNdx);
-                if (!r.finishing && r.okToShowLocked()) {
-                    return r;
-                }
-            }
+        if (getParent() == null) {
+            return null;
         }
-        return null;
+        return getActivity(ActivityRecord::canBeTopRunning);
     }
 
     /**
      * Return true if any activities in this task belongs to input uid.
      */
-    boolean containsAppUid(int uid) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityRecord r = getChildAt(i);
-            if (r.getUid() == uid) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
-        if (mStack != null) {
-            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = getChildAt(activityNdx);
-                if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
-                    outActivities.add(r);
-                }
-            }
-        }
+    boolean isUidPresent(int uid) {
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                ActivityRecord::isUid, PooledLambda.__(ActivityRecord.class), uid);
+        final boolean isUidPresent = getActivity(p) != null;
+        p.recycle();
+        return isUidPresent;
     }
 
     ActivityRecord topRunningActivityWithStartingWindowLocked() {
-        if (mStack != null) {
-            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = getChildAt(activityNdx);
-                if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
-                        || r.finishing || !r.okToShowLocked()) {
-                    continue;
-                }
-                return r;
-            }
+        if (getParent() == null) {
+            return null;
         }
-        return null;
+        return getActivity((r) -> r.mStartingWindowState == STARTING_WINDOW_SHOWN
+                && r.canBeTopRunning());
     }
 
     /**
      * Return the number of running activities, and the number of non-finishing/initializing
      * activities in the provided {@param reportOut} respectively.
      */
-    void getNumRunningActivities(TaskActivitiesReport reportOut) {
+    private void getNumRunningActivities(TaskActivitiesReport reportOut) {
         reportOut.reset();
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityRecord r = getChildAt(i);
-            if (r.finishing) {
-                continue;
-            }
-
-            reportOut.base = r;
-
-            // Increment the total number of non-finishing activities
-            reportOut.numActivities++;
-
-            if (reportOut.top == null || (reportOut.top.isState(ActivityState.INITIALIZING))) {
-                reportOut.top = r;
-                // Reset the number of running activities until we hit the first non-initializing
-                // activity
-                reportOut.numRunning = 0;
-            }
-            if (r.attachedToProcess()) {
-                // Increment the number of actually running activities
-                reportOut.numRunning++;
-            }
-        }
-    }
-
-    boolean okToShowLocked() {
-        // NOTE: If {@link Task#topRunningActivity} return is not null then it is
-        // okay to show the activity when locked.
-        return mAtmService.mStackSupervisor.isCurrentProfileLocked(mUserId)
-                || topRunningActivityLocked() != null;
+        forAllActivities(reportOut);
     }
 
     /**
@@ -1212,10 +1183,11 @@
     }
 
     @Override
-    void addChild(ActivityRecord r, int index) {
+    void addChild(WindowContainer child, int index) {
         // If this task had any child before we added this one.
         boolean hadChild = hasChild();
 
+        final ActivityRecord r = (ActivityRecord) child;
         index = getAdjustedAddPosition(r, index);
         super.addChild(r, index);
 
@@ -1244,9 +1216,6 @@
         }
 
         updateEffectiveIntent();
-        if (r.isPersistable()) {
-            mAtmService.notifyTaskPersisterLocked(this, false);
-        }
 
         // Make sure the list of display UID whitelists is updated
         // now that this record is in a new task.
@@ -1258,16 +1227,13 @@
     }
 
     @Override
-    void removeChild(ActivityRecord r) {
+    void removeChild(WindowContainer r) {
         if (!mChildren.contains(r)) {
             Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this);
             return;
         }
 
         super.removeChild(r);
-        if (r.isPersistable()) {
-            mAtmService.notifyTaskPersisterLocked(this, false);
-        }
 
         if (inPinnedWindowingMode()) {
             // We normally notify listeners of task stack changes on pause, however pinned stack
@@ -1283,7 +1249,7 @@
             // The following block can be executed multiple times if there is more than one overlay.
             // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
             // of the task by id and exiting early if not found.
-            if (onlyHasTaskOverlayActivities(false /* excludingFinishing */)) {
+            if (onlyHasTaskOverlayActivities(true /*includeFinishing*/)) {
                 // When destroying a task, tell the supervisor to remove it so that any activity it
                 // has can be cleaned up correctly. This is currently the only place where we remove
                 // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
@@ -1305,25 +1271,20 @@
 
     /**
      * @return whether or not there are ONLY task overlay activities in the stack.
-     *         If {@param excludeFinishing} is set, then ignore finishing activities in the check.
-     *         If there are no task overlay activities, this call returns false.
+     *         If {@param includeFinishing} is set, then don't ignore finishing activities in the
+     *         check. If there are no task overlay activities, this call returns false.
      */
-    boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
-        int count = 0;
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final ActivityRecord r = getChildAt(i);
-            if (excludeFinishing && r.finishing) {
-                continue;
-            }
-            if (!r.mTaskOverlay) {
-                return false;
-            }
-            count++;
+    boolean onlyHasTaskOverlayActivities(boolean includeFinishing) {
+        if (getChildCount() == 0) {
+            return false;
         }
-        return count > 0;
+        if (includeFinishing) {
+            return getActivity((r) -> r.mTaskOverlay) != null;
+        }
+        return getActivity((r) -> !r.finishing && r.mTaskOverlay) != null;
     }
 
-    boolean autoRemoveFromRecents() {
+    private boolean autoRemoveFromRecents() {
         // We will automatically remove the task either if it has explicitly asked for
         // this, or it is empty and has never contained an activity that got shown to
         // the user.
@@ -1335,24 +1296,21 @@
      * task starting at a specified index.
      */
     private void performClearTaskAtIndexLocked(String reason) {
-        int numActivities = getChildCount();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = getChildAt(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            if (mStack == null) {
+        // Broken down into to cases to avoid object create due to capturing mStack.
+        if (mStack == null) {
+            forAllActivities((r) -> {
+                if (r.finishing) return;
                 // Task was restored from persistent storage.
                 r.takeFromHistory();
                 removeChild(r);
-                --activityNdx;
-                --numActivities;
-            } else if (r.finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                    false /* oomAdj */)
-                    == FINISH_RESULT_REMOVED) {
-                --activityNdx;
-                --numActivities;
-            }
+            });
+        } else {
+            forAllActivities((r) -> {
+                if (r.finishing) return;
+                // TODO: figure-out how to avoid object creation due to capture of reason variable.
+                r.finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
+                        false /* oomAdj */);
+            });
         }
     }
 
@@ -1383,50 +1341,43 @@
      * @return Returns the old activity that should be continued to be used,
      * or {@code null} if none was found.
      */
-    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
-        int numActivities = getChildCount();
-        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord r = getChildAt(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            if (r.mActivityComponent.equals(newR.mActivityComponent)) {
-                // Here it is!  Now finish everything in front...
-                final ActivityRecord ret = r;
+    private ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+        final ActivityRecord r = findActivityInHistory(newR.mActivityComponent);
+        if (r == null) return null;
 
-                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
-                    r = getChildAt(activityNdx);
-                    if (r.finishing) {
-                        continue;
-                    }
-                    ActivityOptions opts = r.takeOptionsLocked(false /* fromClient */);
-                    if (opts != null) {
-                        ret.updateOptionsLocked(opts);
-                    }
-                    if (r.finishIfPossible("clear-task-stack", false /* oomAdj */)
-                            == FINISH_RESULT_REMOVED) {
-                        --activityNdx;
-                        --numActivities;
-                    }
-                }
+        final PooledFunction f = PooledLambda.obtainFunction(Task::finishActivityAbove,
+                PooledLambda.__(ActivityRecord.class), r);
+        forAllActivities(f);
+        f.recycle();
 
-                // Finally, if this is a normal launch mode (that is, not
-                // expecting onNewIntent()), then we will finish the current
-                // instance of the activity so a new fresh one can be started.
-                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
-                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
-                        && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
-                    if (!ret.finishing) {
-                        ret.finishIfPossible("clear-task-top", false /* oomAdj */);
-                        return null;
-                    }
-                }
-
-                return ret;
+        // Finally, if this is a normal launch mode (that is, not expecting onNewIntent()), then we
+        // will finish the current instance of the activity so a new fresh one can be started.
+        if (r.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+                && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
+                && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
+            if (!r.finishing) {
+                r.finishIfPossible("clear-task-top", false /* oomAdj */);
+                return null;
             }
         }
 
-        return null;
+        return r;
+    }
+
+    private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity) {
+        // Stop operation once we reach the boundary activity.
+        if (r == boundaryActivity) return true;
+
+        if (!r.finishing) {
+            final ActivityOptions opts = r.takeOptionsLocked(false /* fromClient */);
+            if (opts != null) {
+                // TODO: Why is this updating the boundary activity vs. the current activity???
+                boundaryActivity.updateOptionsLocked(opts);
+            }
+            r.finishIfPossible("clear-task-stack", false /* oomAdj */);
+        }
+
+        return false;
     }
 
     void removeTaskActivitiesLocked(String reason) {
@@ -1537,140 +1488,83 @@
      * Find the activity in the history stack within the given task.  Returns
      * the index within the history at which it's found, or < 0 if not found.
      */
-    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
-        final ComponentName realActivity = r.mActivityComponent;
-        for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord candidate = getChildAt(activityNdx);
-            if (candidate.finishing) {
-                continue;
-            }
-            if (candidate.mActivityComponent.equals(realActivity)) {
-                return candidate;
-            }
-        }
-        return null;
+    ActivityRecord findActivityInHistory(ComponentName component) {
+        final PooledPredicate p = PooledLambda.obtainPredicate(Task::matchesActivityInHistory,
+                PooledLambda.__(ActivityRecord.class), component);
+        final ActivityRecord r = getActivity(p);
+        p.recycle();
+        return r;
+    }
+
+    private static boolean matchesActivityInHistory(
+            ActivityRecord r, ComponentName activityComponent) {
+        return !r.finishing && r.mActivityComponent.equals(activityComponent);
     }
 
     /** Updates the last task description values. */
     void updateTaskDescription() {
-        // TODO(AM refactor): Cleanup to use findRootIndex()
-        // Traverse upwards looking for any break between main task activities and
-        // utility activities.
-        int activityNdx;
-        final int numActivities = getChildCount();
-        final boolean relinquish = numActivities != 0
-                && (getChildAt(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
-        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = getChildAt(activityNdx);
-            if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
-                // This will be the top activity for determining taskDescription. Pre-inc to
-                // overcome initial decrement below.
-                ++activityNdx;
-                break;
-            }
-            if (r.intent != null
-                    && (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
-                break;
-            }
+        final ActivityRecord root = getRootActivity(true);
+        if (root == null) return;
+
+        final TaskDescription taskDescription = new TaskDescription();
+        final PooledFunction f = PooledLambda.obtainFunction(
+                Task::setTaskDescriptionFromActivityAboveRoot,
+                PooledLambda.__(ActivityRecord.class), root, taskDescription);
+        forAllActivities(f);
+        f.recycle();
+        taskDescription.setResizeMode(mResizeMode);
+        taskDescription.setMinWidth(mMinWidth);
+        taskDescription.setMinHeight(mMinHeight);
+        setTaskDescription(taskDescription);
+        // Update the task affiliation color if we are the parent of the group
+        if (mTaskId == mAffiliatedTaskId) {
+            mAffiliatedTaskColor = taskDescription.getPrimaryColor();
         }
-        if (activityNdx > 0) {
-            // Traverse downwards starting below break looking for set label, icon.
-            // Note that if there are activities in the task but none of them set the
-            // recent activity values, then we do not fall back to the last set
-            // values in the Task.
-            String label = null;
-            String iconFilename = null;
-            int iconResource = -1;
-            int colorPrimary = 0;
-            int colorBackground = 0;
-            int statusBarColor = 0;
-            int navigationBarColor = 0;
-            boolean statusBarContrastWhenTransparent = false;
-            boolean navigationBarContrastWhenTransparent = false;
-            boolean topActivity = true;
-            for (--activityNdx; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = getChildAt(activityNdx);
-                if (r.mTaskOverlay) {
-                    continue;
-                }
-                if (r.taskDescription != null) {
-                    if (label == null) {
-                        label = r.taskDescription.getLabel();
-                    }
-                    if (iconResource == -1) {
-                        iconResource = r.taskDescription.getIconResource();
-                    }
-                    if (iconFilename == null) {
-                        iconFilename = r.taskDescription.getIconFilename();
-                    }
-                    if (colorPrimary == 0) {
-                        colorPrimary = r.taskDescription.getPrimaryColor();
-                    }
-                    if (topActivity) {
-                        colorBackground = r.taskDescription.getBackgroundColor();
-                        statusBarColor = r.taskDescription.getStatusBarColor();
-                        navigationBarColor = r.taskDescription.getNavigationBarColor();
-                        statusBarContrastWhenTransparent =
-                                r.taskDescription.getEnsureStatusBarContrastWhenTransparent();
-                        navigationBarContrastWhenTransparent =
-                                r.taskDescription.getEnsureNavigationBarContrastWhenTransparent();
-                    }
-                }
-                topActivity = false;
-            }
-            final TaskDescription taskDescription = new TaskDescription(label, null, iconResource,
-                    iconFilename, colorPrimary, colorBackground, statusBarColor, navigationBarColor,
-                    statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent,
-                    mResizeMode, mMinWidth, mMinHeight);
-            setTaskDescription(taskDescription);
-            // Update the task affiliation color if we are the parent of the group
-            if (mTaskId == mAffiliatedTaskId) {
-                mAffiliatedTaskColor = taskDescription.getPrimaryColor();
-            }
-            mAtmService.getTaskChangeNotificationController().notifyTaskDescriptionChanged(
-                    getTaskInfo());
-        }
+        mAtmService.getTaskChangeNotificationController().notifyTaskDescriptionChanged(
+                getTaskInfo());
     }
 
-    /**
-     * Find the index of the root activity in the task. It will be the first activity from the
-     * bottom that is not finishing.
-     *
-     * @param effectiveRoot Flag indicating whether 'effective root' should be returned - an
-     *                      activity that defines the task identity (its base intent). It's the
-     *                      first one that does not have
-     *                      {@link ActivityInfo#FLAG_RELINQUISH_TASK_IDENTITY}.
-     * @return index of the 'root' or 'effective' root in the list of activities, -1 if no eligible
-     *         activity was found.
-     */
-    int findRootIndex(boolean effectiveRoot) {
-        int effectiveNdx = -1;
-        final int topActivityNdx = getChildCount() - 1;
-        for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
-            final ActivityRecord r = getChildAt(activityNdx);
-            if (r.finishing) {
-                continue;
+    private static boolean setTaskDescriptionFromActivityAboveRoot(
+            ActivityRecord r, ActivityRecord root, TaskDescription td) {
+        if (!r.mTaskOverlay && r.taskDescription != null) {
+            final TaskDescription atd = r.taskDescription;
+            if (td.getLabel() == null) {
+                td.setLabel(atd.getLabel());
             }
-            effectiveNdx = activityNdx;
-            if (!effectiveRoot || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
-                break;
+            if (td.getIconResource() == 0) {
+                td.setIcon(atd.getIconResource());
             }
+            if (td.getIconFilename() == null) {
+                td.setIconFilename(atd.getIconFilename());
+            }
+            if (td.getPrimaryColor() == 0) {
+                td.setPrimaryColor(atd.getPrimaryColor());
+            }
+            if (td.getBackgroundColor() == 0) {
+                td.setBackgroundColor(atd.getBackgroundColor());
+            }
+            if (td.getStatusBarColor() == 0) {
+                td.setStatusBarColor(atd.getStatusBarColor());
+                td.setEnsureStatusBarContrastWhenTransparent(
+                        atd.getEnsureStatusBarContrastWhenTransparent());
+            }
+            if (td.getNavigationBarColor() == 0) {
+                td.setNavigationBarColor(atd.getNavigationBarColor());
+                td.setEnsureNavigationBarContrastWhenTransparent(
+                        atd.getEnsureNavigationBarContrastWhenTransparent());
+            }
+
         }
-        return effectiveNdx;
+
+        // End search once we get to root.
+        return r == root;
     }
 
     // TODO (AM refactor): Invoke automatically when there is a change in children
     @VisibleForTesting
     void updateEffectiveIntent() {
-        int effectiveRootIndex = findRootIndex(true /* effectiveRoot */);
-        if (effectiveRootIndex == -1) {
-            // All activities in the task are either finishing or relinquish task identity.
-            // But we still want to update the intent, so let's use the bottom activity.
-            effectiveRootIndex = 0;
-        }
-        final ActivityRecord r = getChildAt(effectiveRootIndex);
-        setIntent(r);
-
+        final ActivityRecord root = getRootActivity(true /*setToBottomIfNone*/);
+        setIntent(root);
         // Update the task description when the activities change
         updateTaskDescription();
     }
@@ -2220,15 +2114,6 @@
         return mLastNonFullscreenBounds;
     }
 
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-            final ActivityRecord r = getChildAt(activityNdx);
-            if (r.mVisibleRequested) {
-                r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
-            }
-        }
-    }
-
     void setRootProcess(WindowProcessController proc) {
         clearRootProcess();
         if (intent != null
@@ -2245,12 +2130,6 @@
         }
     }
 
-    void clearAllPendingOptions() {
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            getChildAt(i).clearOptionsLocked(false /* withAbort */);
-        }
-    }
-
     @Override
     DisplayContent getDisplayContent() {
         return getTaskStack() != null ? getTaskStack().getDisplayContent() : null;
@@ -2260,20 +2139,14 @@
         return (ActivityStack) getParent();
     }
 
+    // TODO(task-hierarchy): Needs to take a generic WindowManager when task contains other tasks.
     int getAdjustedAddPosition(ActivityRecord r, int suggestedPosition) {
         int maxPosition = mChildren.size();
         if (!r.mTaskOverlay) {
             // We want to place all non-overlay activities below overlays.
-            while (maxPosition > 0) {
-                final ActivityRecord current = mChildren.get(maxPosition - 1);
-                if (current.mTaskOverlay) {
-                    --maxPosition;
-                    continue;
-                }
-                break;
-            }
-            if (maxPosition < 0) {
-                maxPosition = 0;
+            final ActivityRecord bottomMostOverlay = getActivity((ar) -> ar.mTaskOverlay, false);
+            if (bottomMostOverlay != null) {
+                maxPosition = Math.max(mChildren.indexOf(bottomMostOverlay) - 1, 0);
             }
         }
 
@@ -2281,18 +2154,13 @@
     }
 
     @Override
-    void positionChildAt(int position, ActivityRecord child, boolean includingParents) {
-        position = getAdjustedAddPosition(child, position);
+    void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+        position = getAdjustedAddPosition((ActivityRecord) child, position);
         super.positionChildAt(position, child, includingParents);
     }
 
     private boolean hasWindowsAlive() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            if (mChildren.get(i).hasWindowsAlive()) {
-                return true;
-            }
-        }
-        return false;
+        return getActivity(ActivityRecord::hasWindowsAlive) != null;
     }
 
     @VisibleForTesting
@@ -2522,26 +2390,21 @@
      * @param out Rect containing the max visible bounds.
      * @return true if the task has some visible app windows; false otherwise.
      */
-    private boolean getMaxVisibleBounds(Rect out) {
-        boolean foundTop = false;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord token = mChildren.get(i);
-            // skip hidden (or about to hide) apps
-            if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
-                continue;
-            }
-            final WindowState win = token.findMainWindow();
-            if (win == null) {
-                continue;
-            }
-            if (!foundTop) {
-                foundTop = true;
-                out.setEmpty();
-            }
-
-            win.getMaxVisibleBounds(out);
+    private static void getMaxVisibleBounds(ActivityRecord token, Rect out, boolean[] foundTop) {
+        // skip hidden (or about to hide) apps
+        if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
+            return;
         }
-        return foundTop;
+        final WindowState win = token.findMainWindow();
+        if (win == null) {
+            return;
+        }
+        if (!foundTop[0]) {
+            foundTop[0] = true;
+            out.setEmpty();
+        }
+
+        win.getMaxVisibleBounds(out);
     }
 
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
@@ -2551,8 +2414,14 @@
         // a DimLayer anyway if we weren't visible.
         final boolean dockedResizing = displayContent != null
                 && displayContent.mDividerControllerLocked.isResizing();
-        if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
-            return;
+        if (inFreeformWindowingMode()) {
+            boolean[] foundTop = { false };
+            final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
+                    PooledLambda.__(ActivityRecord.class), out, foundTop);
+            c.recycle();
+            if (foundTop[0]) {
+                return;
+            }
         }
 
         if (!matchParentBounds()) {
@@ -2658,8 +2527,9 @@
     }
 
     boolean showForAllUsers() {
-        final int tokensCount = mChildren.size();
-        return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
+        if (mChildren.isEmpty()) return false;
+        final ActivityRecord r = getTopNonFinishingActivity();
+        return r != null && r.mShowForAllUsers;
     }
 
     /**
@@ -2733,24 +2603,17 @@
     }
 
     ActivityRecord getTopFullscreenActivity() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mChildren.get(i);
-            final WindowState win = activity.findMainWindow();
-            if (win != null && win.mAttrs.isFullscreen()) {
-                return activity;
-            }
-        }
-        return null;
+        return getActivity((r) -> {
+            final WindowState win = r.findMainWindow();
+            return (win != null && win.mAttrs.isFullscreen());
+        });
     }
 
     ActivityRecord getTopVisibleActivity() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mChildren.get(i);
-            if (!activity.mIsExiting && activity.isClientVisible() && activity.mVisibleRequested) {
-                return activity;
-            }
-        }
-        return null;
+        return getActivity((r) -> {
+            // skip hidden (or about to hide) apps
+            return !r.mIsExiting && r.isClientVisible() && r.mVisibleRequested;
+        });
     }
 
     void positionChildAtTop(ActivityRecord child) {
@@ -2806,6 +2669,13 @@
         return callback.apply(this);
     }
 
+    @Override
+    Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) {
+        // I'm a task!
+        // TODO(task-hierarchy): Change to traverse children when tasks can contain other tasks.
+        return callback.test(this) ? this : null;
+    }
+
     /**
      * @param canAffectSystemUiFlags If false, all windows in this task can not affect SystemUI
      *                               flags. See {@link WindowState#canAffectSystemUiFlags()}.
@@ -2861,10 +2731,9 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
         proto.write(TaskProto.ID, mTaskId);
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mChildren.get(i);
-            activity.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
-        }
+        forAllActivities((r) -> {
+            r.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
+        });
         proto.write(FILLS_PARENT, matchParentBounds());
         getBounds().writeToProto(proto, TaskProto.BOUNDS);
         mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
@@ -2888,11 +2757,11 @@
         final String triplePrefix = doublePrefix + "  ";
         final String quadruplePrefix = triplePrefix + "  ";
 
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mChildren.get(i);
-            pw.println(triplePrefix + "Activity #" + i + " " + activity);
-            activity.dump(pw, quadruplePrefix, dumpAll);
-        }
+        int[] index = { 0 };
+        forAllActivities((r) -> {
+            pw.println(triplePrefix + "Activity #" + index[0]++ + " " + r);
+            r.dump(pw, quadruplePrefix, dumpAll);
+        });
     }
 
     /**
@@ -3071,10 +2940,10 @@
         final long token = proto.start(fieldId);
         writeToProtoInnerTaskOnly(proto, TASK, logLevel);
         proto.write(com.android.server.am.TaskRecordProto.ID, mTaskId);
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final ActivityRecord activity = getChildAt(i);
-            activity.writeToProto(proto, ACTIVITIES);
-        }
+
+        forAllActivities((r) -> {
+            r.writeToProto(proto, ACTIVITIES);
+        });
         proto.write(STACK_ID, getStackId());
         if (mLastNonFullscreenBounds != null) {
             mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS);
@@ -3100,7 +2969,7 @@
     }
 
     /** @see #getNumRunningActivities(TaskActivitiesReport) */
-    static class TaskActivitiesReport {
+    static class TaskActivitiesReport implements Consumer<ActivityRecord> {
         int numRunning;
         int numActivities;
         ActivityRecord top;
@@ -3110,12 +2979,35 @@
             numRunning = numActivities = 0;
             top = base = null;
         }
+
+        @Override
+        public void accept(ActivityRecord r) {
+            if (r.finishing) {
+                return;
+            }
+
+            base = r;
+
+            // Increment the total number of non-finishing activities
+            numActivities++;
+
+            if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+                top = r;
+                // Reset the number of running activities until we hit the first non-initializing
+                // activity
+                numRunning = 0;
+            }
+            if (r.attachedToProcess()) {
+                // Increment the number of actually running activities
+                numRunning++;
+            }
+        }
     }
 
     /**
      * Saves this {@link Task} to XML using given serializer.
      */
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+    void saveToXml(XmlSerializer out) throws Exception {
         if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
 
         out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId));
@@ -3181,19 +3073,33 @@
             out.endTag(null, TAG_INTENT);
         }
 
-        final int numActivities = getChildCount();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = getChildAt(activityNdx);
-            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
-                    || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
-                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
-                    && activityNdx > 0) {
-                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
-                break;
-            }
+        sTmpException = null;
+        final PooledFunction f = PooledLambda.obtainFunction(Task::saveActivityToXml,
+                PooledLambda.__(ActivityRecord.class), getBottomMostActivity(), out);
+        forAllActivities(f);
+        f.recycle();
+        if (sTmpException != null) {
+            throw sTmpException;
+        }
+    }
+
+    private static boolean saveActivityToXml(
+            ActivityRecord r, ActivityRecord first, XmlSerializer out) {
+        if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
+                || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
+                && r != first) {
+            // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
+            return true;
+        }
+        try {
             out.startTag(null, TAG_ACTIVITY);
             r.saveToXml(out);
             out.endTag(null, TAG_ACTIVITY);
+            return false;
+        } catch (Exception e) {
+            sTmpException = e;
+            return true;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 1e2f0d0..eb130a1 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -514,7 +514,7 @@
             mService = service;
         }
 
-        private StringWriter saveToXml(Task task) throws IOException, XmlPullParserException {
+        private StringWriter saveToXml(Task task) throws Exception {
             if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
             final XmlSerializer xmlSerializer = new FastXmlSerializer();
             StringWriter stringWriter = new StringWriter();
@@ -550,8 +550,7 @@
                     try {
                         if (DEBUG) Slog.d(TAG, "Saving task=" + task);
                         stringWriter = saveToXml(task);
-                    } catch (IOException e) {
-                    } catch (XmlPullParserException e) {
+                    } catch (Exception e) {
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index d07516a..5cbab5d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -48,9 +48,9 @@
         if (entry != null) {
             mAppTaskMap.remove(entry.topApp);
         }
-        final ActivityRecord top = task.getTopChild();
+        final ActivityRecord top = task.getTopMostActivity();
         mAppTaskMap.put(top, task.mTaskId);
-        mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
+        mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f83ceb0..c1a36c4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -235,23 +235,18 @@
      * children, which should be ignored.
      */
     @Nullable private ActivityRecord findAppTokenForSnapshot(Task task) {
-        for (int i = task.getChildCount() - 1; i >= 0; --i) {
-            final ActivityRecord activity = task.getChildAt(i);
-            if (activity == null || !activity.isSurfaceShowing()
-                    || activity.findMainWindow() == null) {
-                continue;
+        return task.getActivity((r) -> {
+            if (r == null || !r.isSurfaceShowing() || r.findMainWindow() == null) {
+                return false;
             }
-            final boolean hasVisibleChild = activity.forAllWindows(
+            return r.forAllWindows(
                     // Ensure at least one window for the top app is visible before attempting to
                     // take a screenshot. Visible here means that the WSA surface is shown and has
                     // an alpha greater than 0.
                     ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
                             && ws.mWinAnimator.mLastAlpha > 0f, true  /* traverseTopToBottom */);
-            if (hasVisibleChild) {
-                return activity;
-            }
-        }
-        return null;
+
+        });
     }
 
     @Nullable
@@ -380,7 +375,7 @@
 
     @VisibleForTesting
     int getSnapshotMode(Task task) {
-        final ActivityRecord topChild = task.getTopChild();
+        final ActivityRecord topChild = task.getTopMostActivity();
         if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
             return SNAPSHOT_MODE_NONE;
         } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
@@ -395,7 +390,7 @@
      * as possible by using the theme's window background.
      */
     private TaskSnapshot drawAppThemeSnapshot(Task task) {
-        final ActivityRecord topChild = task.getTopChild();
+        final ActivityRecord topChild = task.getTopMostActivity();
         if (topChild == null) {
             return null;
         }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 453514b2..5c1491e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -310,6 +310,10 @@
     final protected void setParent(WindowContainer<WindowContainer> parent) {
         final WindowContainer oldParent = mParent;
         mParent = parent;
+
+        if (mParent != null) {
+            mParent.onChildAdded(this);
+        }
         if (!mReparenting) {
             onParentChanged(mParent, oldParent);
         }
@@ -389,7 +393,6 @@
         } else {
             mChildren.add(positionToAdd, child);
         }
-        onChildAdded(child);
 
         // Set the parent after we've actually added a child in case a subclass depends on this.
         child.setParent(this);
@@ -418,7 +421,6 @@
         }
 
         mChildren.add(index, child);
-        onChildAdded(child);
 
         // Set the parent after we've actually added a child in case a subclass depends on this.
         child.setParent(this);
@@ -1096,11 +1098,22 @@
     }
 
     boolean forAllActivities(Function<ActivityRecord, Boolean> callback) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            if (mChildren.get(i).forAllActivities(callback)) {
-                return true;
+        return forAllActivities(callback, true /*traverseTopToBottom*/);
+    }
+
+    boolean forAllActivities(
+            Function<ActivityRecord, Boolean> callback, boolean traverseTopToBottom) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                if (mChildren.get(i).forAllActivities(callback, traverseTopToBottom)) return true;
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                if (mChildren.get(i).forAllActivities(callback, traverseTopToBottom)) return true;
             }
         }
+
         return false;
     }
 
@@ -1121,6 +1134,61 @@
         }
     }
 
+    /**
+     * Process all activities in this branch of the tree.
+     *
+     * @param callback Called for each activity found.
+     * @param boundary We don't return activities via {@param callback} until we get to this node in
+     *                 the tree.
+     * @param includeBoundary If the boundary from be processed to return activities.
+     * @param traverseTopToBottom direction to traverse the tree.
+     * @return {@code true} if we ended the search before reaching the end of the tree.
+     */
+    final boolean forAllActivities(Function<ActivityRecord, Boolean> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom) {
+        return forAllActivities(
+                callback, boundary, includeBoundary, traverseTopToBottom, new boolean[1]);
+    }
+
+    private boolean forAllActivities(Function<ActivityRecord, Boolean> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                if (processForAllActivitiesWithBoundary(callback, boundary, includeBoundary,
+                        traverseTopToBottom, boundaryFound, mChildren.get(i))) {
+                    return true;
+                }
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                if (processForAllActivitiesWithBoundary(callback, boundary, includeBoundary,
+                        traverseTopToBottom, boundaryFound, mChildren.get(i))) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private boolean processForAllActivitiesWithBoundary(Function<ActivityRecord, Boolean> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound, WindowContainer wc) {
+        if (wc == boundary) {
+            boundaryFound[0] = true;
+            if (!includeBoundary) return false;
+        }
+
+        if (boundaryFound[0]) {
+            return wc.forAllActivities(callback, traverseTopToBottom);
+        }
+
+        return wc.forAllActivities(
+                callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
+    }
+
     /** @return {@code true} if this node or any of its children contains an activity. */
     boolean hasActivity() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
@@ -1156,6 +1224,81 @@
         return null;
     }
 
+    /**
+     * Gets an activity in a branch of the tree.
+     *
+     * @param callback called to test if this is the activity that should be returned.
+     * @param boundary We don't return activities via {@param callback} until we get to this node in
+     *                 the tree.
+     * @param includeBoundary If the boundary from be processed to return activities.
+     * @param traverseTopToBottom direction to traverse the tree.
+     * @return The activity if found or null.
+     */
+    final ActivityRecord getActivity(Predicate<ActivityRecord> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom) {
+        return getActivity(
+                callback, boundary, includeBoundary, traverseTopToBottom, new boolean[1]);
+    }
+
+    private ActivityRecord getActivity(Predicate<ActivityRecord> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = processGetActivityWithBoundary(callback, boundary,
+                        includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+                if (r != null) {
+                    return r;
+                }
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                final ActivityRecord r = processGetActivityWithBoundary(callback, boundary,
+                        includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+                if (r != null) {
+                    return r;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private ActivityRecord processGetActivityWithBoundary(Predicate<ActivityRecord> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound, WindowContainer wc) {
+        if (wc == boundary || boundary == null) {
+            boundaryFound[0] = true;
+            if (!includeBoundary) return null;
+        }
+
+        if (boundaryFound[0]) {
+            return wc.getActivity(callback, traverseTopToBottom);
+        }
+
+        return wc.getActivity(
+                callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
+    }
+
+    ActivityRecord getActivityAbove(ActivityRecord r) {
+        return getActivity((above) -> true, r,
+                false /*includeBoundary*/, false /*traverseTopToBottom*/);
+    }
+
+    ActivityRecord getActivityBelow(ActivityRecord r) {
+        return getActivity((below) -> true, r,
+                false /*includeBoundary*/, true /*traverseTopToBottom*/);
+    }
+
+    ActivityRecord getBottomMostActivity() {
+        return getActivity((r) -> true, false /*traverseTopToBottom*/);
+    }
+
+    ActivityRecord getTopMostActivity() {
+        return getActivity((r) -> true, true /*traverseTopToBottom*/);
+    }
+
     ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
         // Break down into 4 calls to avoid object creation due to capturing input params.
         if (includeFinishing) {
@@ -1187,6 +1330,31 @@
         }
     }
 
+    Task getTask(Predicate<Task> callback) {
+        return getTask(callback, true /*traverseTopToBottom*/);
+    }
+
+    Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task t = mChildren.get(i).getTask(callback, traverseTopToBottom);
+                if (t != null) {
+                    return t;
+                }
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                final Task t = mChildren.get(i).getTask(callback, traverseTopToBottom);
+                if (t != null) {
+                    return t;
+                }
+            }
+        }
+
+        return null;
+    }
+
     /**
      * For all tasks at or below this container call the callback.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a6578f3..4ec50f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2350,8 +2350,9 @@
 
             win.setLastReportedMergedConfiguration(mergedConfiguration);
 
-            // Update the last inset values here because the values are sent back to the client.
-            // The last inset values represent the last client state.
+            // Update the last frames and inset values here because the values are sent back to the
+            // client. The last values represent the last client state.
+            win.updateLastFrames();
             win.updateLastInsetValues();
 
             win.getCompatFrame(outFrame);
@@ -3545,8 +3546,12 @@
         }
     }
 
-    void setRotateForApp(int displayId,
-            @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
+    @Override
+    public void setFixedToUserRotation(int displayId, int fixedToUserRotation) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+                "freezeRotation()")) {
+            throw new SecurityException("Requires SET_ORIENTATION permission");
+        }
         synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
@@ -6791,19 +6796,23 @@
                 "setShouldShowWithInsecureKeyguard()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: "
+                            + "%d", displayId);
+                    return;
+                }
 
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-            if (displayContent == null) {
-                ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: %d",
-                        displayId);
-                return;
+                mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent,
+                        shouldShow);
+
+                displayContent.reconfigureDisplayLocked();
             }
-
-            mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent,
-                    shouldShow);
-
-            displayContent.reconfigureDisplayLocked();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -6832,22 +6841,26 @@
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR, "Attempted to set system decors flag to a display that "
+                            + "does not exist: %d", displayId);
+                    return;
+                }
+                if (displayContent.isUntrustedVirtualDisplay()) {
+                    throw new SecurityException("Attempted to set system decors flag to an "
+                            + "untrusted virtual display: " + displayId);
+                }
 
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-            if (displayContent == null) {
-                ProtoLog.w(WM_ERROR, "Attempted to set system decors flag to a display that does "
-                        + "not exist: %d", displayId);
-                return;
+                mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow);
+
+                displayContent.reconfigureDisplayLocked();
             }
-            if (displayContent.isUntrustedVirtualDisplay()) {
-                throw new SecurityException("Attempted to set system decors flag to an untrusted "
-                        + "virtual display: " + displayId);
-            }
-
-            mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow);
-
-            displayContent.reconfigureDisplayLocked();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -6878,23 +6891,26 @@
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowIme()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR, "Attempted to set IME flag to a display that does not "
+                            + "exist: %d", displayId);
+                    return;
+                }
+                if (displayContent.isUntrustedVirtualDisplay()) {
+                    throw new SecurityException("Attempted to set IME flag to an untrusted "
+                            + "virtual display: " + displayId);
+                }
 
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-            if (displayContent == null) {
-                ProtoLog.w(WM_ERROR,
-                        "Attempted to set IME flag to a display that does not exist: %d",
-                        displayId);
-                return;
+                mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow);
+
+                displayContent.reconfigureDisplayLocked();
             }
-            if (displayContent.isUntrustedVirtualDisplay()) {
-                throw new SecurityException("Attempted to set IME flag to an untrusted "
-                        + "virtual display: " + displayId);
-            }
-
-            mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow);
-
-            displayContent.reconfigureDisplayLocked();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 5da3eb6..619d87b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -321,7 +321,7 @@
         }
     }
 
-    private int runSetFixToUserRotation(PrintWriter pw) {
+    private int runSetFixToUserRotation(PrintWriter pw) throws RemoteException {
         int displayId = Display.DEFAULT_DISPLAY;
         String arg = getNextArgRequired();
         if ("-d".equals(arg)) {
@@ -329,16 +329,16 @@
             arg = getNextArgRequired();
         }
 
-        final @DisplayRotation.FixedToUserRotation  int fixedToUserRotation;
+        final int fixedToUserRotation;
         switch (arg) {
             case "enabled":
-                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
+                fixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
                 break;
             case "disabled":
-                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
+                fixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
                 break;
             case "default":
-                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
+                fixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
                 break;
             default:
                 getErrPrintWriter().println("Error: expecting enabled, disabled or default, but we "
@@ -346,7 +346,7 @@
                 return -1;
         }
 
-        mInternal.setRotateForApp(displayId, fixedToUserRotation);
+        mInterface.setFixedToUserRotation(displayId, fixedToUserRotation);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c1783ef..e169037 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -722,11 +722,10 @@
         return true;
     }
 
-    ArraySet<Task> getReleaseSomeActivitiesTasks() {
+    void releaseSomeActivities(String reason) {
         // Examine all activities currently running in the process.
-        Task firstTask = null;
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<Task> tasks = null;
+        // Candidate activities that can be destroyed.
+        ArrayList<ActivityRecord> candidates = null;
         if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
         for (int i = 0; i < mActivities.size(); i++) {
             final ActivityRecord r = mActivities.get(i);
@@ -735,33 +734,37 @@
             // down before we try to prune more activities.
             if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
-                return null;
+                return;
             }
             // Don't consider any activities that are currently not in a state where they
             // can be destroyed.
-            if (r.mVisibleRequested || !r.stopped || !r.hasSavedState()
+            if (r.mVisibleRequested || !r.stopped || !r.hasSavedState() || !r.isDestroyable()
                     || r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
                 continue;
             }
 
-            final Task task = r.getTask();
-            if (task != null) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
-                        + " from " + r);
-                if (firstTask == null) {
-                    firstTask = task;
-                } else if (firstTask != task) {
-                    if (tasks == null) {
-                        tasks = new ArraySet<>();
-                        tasks.add(firstTask);
-                    }
-                    tasks.add(task);
+            if (r.getParent() != null) {
+                if (candidates == null) {
+                    candidates = new ArrayList<>();
                 }
+                candidates.add(r);
             }
         }
 
-        return tasks;
+        if (candidates != null) {
+            // Sort based on z-order in hierarchy.
+            candidates.sort(WindowContainer::compareTo);
+            // Release some older activities
+            int maxRelease = Math.max(candidates.size(), 1);
+            do {
+                final ActivityRecord r = candidates.remove(0);
+                if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + r
+                        + " in state " + r.getState() + " for reason " + reason);
+                r.destroyImmediately(true /*removeFromApp*/, reason);
+                --maxRelease;
+            } while (maxRelease > 0);
+        }
     }
 
     public interface ComputeOomAdjCallback {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0441669..ce26e5f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -983,7 +983,7 @@
                             - mWindowFrames.mVisibleFrame.bottom;
                     if (bottomOverlap > 0) {
                         final int distanceToTop = Math.max(mWindowFrames.mContainingFrame.top
-                                - mWindowFrames.mDisplayFrame.top, 0);
+                                - mWindowFrames.mContentFrame.top, 0);
                         int offs = Math.min(bottomOverlap, distanceToTop);
                         mWindowFrames.mContainingFrame.top -= offs;
                     }
@@ -1308,8 +1308,7 @@
 
         // We update mLastFrame always rather than in the conditional with the last inset
         // variables, because mFrameSizeChanged only tracks the width and height changing.
-        mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
-        mWindowFrames.mLastRelFrame.set(mWindowFrames.mRelFrame);
+        updateLastFrames();
 
         if (didFrameInsetsChange
                 || winAnimator.mSurfaceResized
@@ -4817,6 +4816,12 @@
         return mEmbeddedDisplayContents.remove(dc);
     }
 
+    /** Updates the last frames and relative frames to the current ones. */
+    void updateLastFrames() {
+        mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
+        mWindowFrames.mLastRelFrame.set(mWindowFrames.mRelFrame);
+    }
+
     /**
      * Updates the last inset values to the current ones.
      */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 471164d..fd8094c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -124,6 +124,7 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss@2.1",
         "android.hardware.gnss.measurement_corrections@1.0",
         "android.hardware.gnss.visibility_control@1.0",
         "android.hardware.input.classifier@1.0",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a0f5628..6504e31 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -21,10 +21,12 @@
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/1.1/IGnss.h>
 #include <android/hardware/gnss/2.0/IGnss.h>
+#include <android/hardware/gnss/2.1/IGnss.h>
 
 #include <android/hardware/gnss/1.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 #include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
 #include <nativehelper/JNIHelp.h>
@@ -140,7 +142,6 @@
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
 
 using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
-using android::hardware::gnss::V2_0::IGnssCallback;
 
 using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
 using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
@@ -153,7 +154,10 @@
 using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
 using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
 using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
+using IGnss_V2_1 = android::hardware::gnss::V2_1::IGnss;
 using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
+using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
 using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
 using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
 using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
@@ -162,9 +166,11 @@
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
+using IGnssMeasurement_V2_1 = android::hardware::gnss::V2_1::IGnssMeasurement;
 using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
+using IGnssMeasurementCallback_V2_1 = android::hardware::gnss::V2_1::IGnssMeasurementCallback;
 using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
 using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
 using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
@@ -201,6 +207,7 @@
 sp<IGnss_V1_0> gnssHal = nullptr;
 sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
 sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
+sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
 sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
 sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
@@ -218,6 +225,7 @@
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
 sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
+sp<IGnssMeasurement_V2_1> gnssMeasurementIface_V2_1 = nullptr;
 sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
 sp<IMeasurementCorrections> gnssCorrectionsIface = nullptr;
 sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
@@ -582,9 +590,9 @@
 /*
  * GnssCallback class implements the callback methods for IGnss interface.
  */
-struct GnssCallback : public IGnssCallback {
+struct GnssCallback : public IGnssCallback_V2_1 {
     Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
-    Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
+    Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override;
     Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
         return gnssSvStatusCbImpl(svStatus);
     }
@@ -595,7 +603,7 @@
     Return<void> gnssRequestTimeCb() override;
     Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
 
-    Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
+    Return<void> gnssSetSystemInfoCb(const IGnssCallback_V1_0::GnssSystemInfo& info) override;
 
     // New in 1.1
     Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
@@ -605,10 +613,12 @@
             override;
     Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
     Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
-    Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList) override {
+    Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
         return gnssSvStatusCbImpl(svInfoList);
     }
-
+    Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
+        return gnssSvStatusCbImpl(svInfoList);
+    }
     Return<void> gnssSetCapabilitesCbImpl(uint32_t capabilities, bool hasSubHalCapabilityFlags);
 
     // TODO: Reconsider allocation cost vs threadsafety on these statics
@@ -625,7 +635,11 @@
         return svStatus.numSvs;
     }
 
-    uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList) {
+    uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) {
+        return svInfoList.size();
+    }
+
+    uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) {
         return svInfoList.size();
     }
 
@@ -635,17 +649,28 @@
     }
 
     const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
-            const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+            const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
         return svInfoList[i].v1_0;
     }
 
+    const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+            const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+        // TODO(b/144850155): fill baseband CN0 after it's available in Java object.
+        ALOGD("getGnssSvInfoOfIndex %d: baseband C/N0: %f", (int) i, svInfoList[i].basebandCN0DbHz);
+        return svInfoList[i].v2_0.v1_0;
+    }
+
     uint32_t getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
         return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
     }
 
-    uint32_t getConstellationType(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+    uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
         return static_cast<uint32_t>(svInfoList[i].constellation);
     }
+
+    uint32_t getConstellationType(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+        return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
+    }
 };
 
 Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
@@ -686,7 +711,7 @@
     return gnssLocationCbImpl<GnssLocation_V2_0>(location);
 }
 
-Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
+Return<void> GnssCallback::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) {
     JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -808,7 +833,7 @@
     return Void();
 }
 
-Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) {
+Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) {
     ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
 
     JNIEnv* env = getJniEnv();
@@ -997,7 +1022,9 @@
  * GnssMeasurementCallback implements the callback methods required for the
  * GnssMeasurement interface.
  */
-struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 {
+struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_1 {
+    Return<void> gnssMeasurementCb_2_1(const IGnssMeasurementCallback_V2_1::GnssData& data)
+            override;
     Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data)
             override;
     Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
@@ -1021,6 +1048,12 @@
     void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
 };
 
+Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_1(
+        const IGnssMeasurementCallback_V2_1::GnssData& data) {
+    translateAndSetGnssData(data);
+    return Void();
+}
+
 Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
         const IGnssMeasurementCallback_V2_0::GnssData& data) {
     translateAndSetGnssData(data);
@@ -1142,6 +1175,17 @@
     SET(ConstellationType, static_cast<int32_t>(measurement_V2_0->constellation));
 }
 
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V2_1::GnssMeasurement>(
+        const IGnssMeasurementCallback_V2_1::GnssMeasurement* measurement_V2_1,
+        JavaObject& object) {
+    translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object);
+    // TODO(b/144850155): fill baseband CN0 after it's available in Java object
+    ALOGD("baseband CN0DbHz = %f\n", measurement_V2_1->basebandCN0DbHz);
+}
+
 template<class T>
 void GnssMeasurementCallback::translateGnssClock(JavaObject& object, const T& data) {
     translateGnssClock(object, data.clock);
@@ -1523,6 +1567,17 @@
 
 /* Initializes the GNSS service handle. */
 static void android_location_GnssLocationProvider_set_gps_service_handle() {
+    ALOGD("Trying IGnss_V2_1::getService()");
+    gnssHal_V2_1 = IGnss_V2_1::getService();
+    if (gnssHal_V2_1 != nullptr) {
+        gnssHal = gnssHal_V2_1;
+        gnssHal_V2_0 = gnssHal_V2_1;
+        gnssHal_V1_1 = gnssHal_V2_1;
+        gnssHal = gnssHal_V2_1;
+        return;
+    }
+
+    ALOGD("gnssHal 2.1 was null, trying 2.0");
     gnssHal_V2_0 = IGnss_V2_0::getService();
     if (gnssHal_V2_0 != nullptr) {
         gnssHal = gnssHal_V2_0;
@@ -1751,18 +1806,30 @@
     }
 
     // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
+    // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement
     // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement
     // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement
     // 1.0@IGnss is paired with 1.0@IGnssMeasurement
     gnssMeasurementIface = nullptr;
-    if (gnssHal_V2_0 != nullptr) {
+    if (gnssHal_V2_1 != nullptr) {
+        auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1();
+        if (!gnssMeasurement.isOk()) {
+            ALOGD("Unable to get a handle to GnssMeasurement_V2_1");
+        } else {
+            gnssMeasurementIface_V2_1 = gnssMeasurement;
+            gnssMeasurementIface_V2_0 = gnssMeasurementIface_V2_1;
+            gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
+            gnssMeasurementIface = gnssMeasurementIface_V1_1;
+        }
+    }
+    if (gnssHal_V2_0 != nullptr && gnssMeasurementIface == nullptr) {
         auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
         if (!gnssMeasurement.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
         } else {
             gnssMeasurementIface_V2_0 = gnssMeasurement;
             gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
-            gnssMeasurementIface = gnssMeasurementIface_V2_0;
+            gnssMeasurementIface = gnssMeasurementIface_V1_1;
         }
     }
     if (gnssHal_V1_1 != nullptr && gnssMeasurementIface == nullptr) {
@@ -1930,8 +1997,10 @@
     Return<bool> result = false;
 
     // Set top level IGnss.hal callback.
-    sp<IGnssCallback> gnssCbIface = new GnssCallback();
-    if (gnssHal_V2_0 != nullptr) {
+    sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
+    if (gnssHal_V2_1 != nullptr) {
+        result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
+    } else if (gnssHal_V2_0 != nullptr) {
         result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
     } else if (gnssHal_V1_1 != nullptr) {
         result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
@@ -2564,7 +2633,9 @@
     sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
     Return<IGnssMeasurement_V1_0::GnssMeasurementStatus> result =
             IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;
-    if (gnssMeasurementIface_V2_0 != nullptr) {
+    if (gnssMeasurementIface_V2_1 != nullptr) {
+        result = gnssMeasurementIface_V2_1->setCallback_2_1(cbIface, enableFullTracking);
+    } else if (gnssMeasurementIface_V2_0 != nullptr) {
         result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking);
     } else if (gnssMeasurementIface_V1_1 != nullptr) {
         result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5a946f2..66f01f3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1114,9 +1114,10 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
-        } catch (RuntimeException e) {
+        } catch (Throwable e) {
             Slog.e("System", "******************************************");
-            Slog.e("System", "************ Failure starting core service", e);
+            Slog.e("System", "************ Failure starting core service");
+            throw e;
         }
 
         // Before things start rolling, be sure we have decided whether
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index c1bbb30..fb42507 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -34,7 +34,7 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
index 73279cb5..adef02e 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -19,75 +19,105 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(AndroidJUnit4.class)
 public class FakeIcingTest {
     @Test
     public void query() {
         FakeIcing icing = new FakeIcing();
-        icing.put("uri:cat", "The cat said meow");
-        icing.put("uri:dog", "The dog said woof");
+        icing.put(createDoc("uri:cat", "The cat said meow"));
+        icing.put(createDoc("uri:dog", "The dog said woof"));
 
-        assertThat(icing.query("meow")).containsExactly("uri:cat");
-        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
-        assertThat(icing.query("fred")).isEmpty();
+        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+        assertThat(queryGetUris(icing, "fred")).isEmpty();
     }
 
     @Test
     public void queryNorm() {
         FakeIcing icing = new FakeIcing();
-        icing.put("uri:cat", "The cat said meow");
-        icing.put("uri:dog", "The dog said woof");
+        icing.put(createDoc("uri:cat", "The cat said meow"));
+        icing.put(createDoc("uri:dog", "The dog said woof"));
 
-        assertThat(icing.query("the")).containsExactly("uri:cat", "uri:dog");
-        assertThat(icing.query("The")).containsExactly("uri:cat", "uri:dog");
-        assertThat(icing.query("tHe")).containsExactly("uri:cat", "uri:dog");
+        assertThat(queryGetUris(icing, "the")).containsExactly("uri:cat", "uri:dog");
+        assertThat(queryGetUris(icing, "The")).containsExactly("uri:cat", "uri:dog");
+        assertThat(queryGetUris(icing, "tHe")).containsExactly("uri:cat", "uri:dog");
     }
 
     @Test
     public void get() {
+        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
         FakeIcing icing = new FakeIcing();
-        icing.put("uri:cat", "The cat said meow");
-        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
+        icing.put(cat);
+        assertThat(icing.get("uri:cat")).isEqualTo(cat);
     }
 
     @Test
     public void replace() {
-        FakeIcing icing = new FakeIcing();
-        icing.put("uri:cat", "The cat said meow");
-        icing.put("uri:dog", "The dog said woof");
+        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
+        DocumentProto dog = createDoc("uri:dog", "The dog said woof");
 
-        assertThat(icing.query("meow")).containsExactly("uri:cat");
-        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
-        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
+        FakeIcing icing = new FakeIcing();
+        icing.put(cat);
+        icing.put(dog);
+
+        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+        assertThat(icing.get("uri:cat")).isEqualTo(cat);
 
         // Replace
-        icing.put("uri:cat", "The cat said purr");
-        icing.put("uri:bird", "The cat said tweet");
+        DocumentProto cat2 = createDoc("uri:cat", "The cat said purr");
+        DocumentProto bird = createDoc("uri:bird", "The cat said tweet");
+        icing.put(cat2);
+        icing.put(bird);
 
-        assertThat(icing.query("meow")).isEmpty();
-        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog", "uri:bird");
-        assertThat(icing.get("uri:cat")).isEqualTo("The cat said purr");
+        assertThat(queryGetUris(icing, "meow")).isEmpty();
+        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog", "uri:bird");
+        assertThat(icing.get("uri:cat")).isEqualTo(cat2);
     }
 
     @Test
     public void delete() {
-        FakeIcing icing = new FakeIcing();
-        icing.put("uri:cat", "The cat said meow");
-        icing.put("uri:dog", "The dog said woof");
+        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
+        DocumentProto dog = createDoc("uri:dog", "The dog said woof");
 
-        assertThat(icing.query("meow")).containsExactly("uri:cat");
-        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
-        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
+        FakeIcing icing = new FakeIcing();
+        icing.put(cat);
+        icing.put(dog);
+
+        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+        assertThat(icing.get("uri:cat")).isEqualTo(cat);
 
         // Delete
         icing.delete("uri:cat");
         icing.delete("uri:notreal");
 
-        assertThat(icing.query("meow")).isEmpty();
-        assertThat(icing.query("said")).containsExactly("uri:dog");
+        assertThat(queryGetUris(icing, "meow")).isEmpty();
+        assertThat(queryGetUris(icing, "said")).containsExactly("uri:dog");
         assertThat(icing.get("uri:cat")).isNull();
     }
+
+    private static DocumentProto createDoc(String uri, String body) {
+        return DocumentProto.newBuilder()
+                .setUri(uri)
+                .addProperties(PropertyProto.newBuilder().addStringValues(body))
+                .build();
+    }
+
+    private static List<String> queryGetUris(FakeIcing icing, String term) {
+        List<String> uris = new ArrayList<>();
+        for (DocumentProto result : icing.query(term)) {
+            uris.add(result.getUri());
+        }
+        return uris;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
new file mode 100644
index 0000000..269f918
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.SparseArray;
+import android.view.Display;
+
+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;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayModeDirectorTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange(
+            int minFps, int maxFps) {
+        DisplayModeDirector director =
+                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+        int displayId = 0;
+        int numModes = maxFps - minFps + 1;
+        Display.Mode[] modes = new Display.Mode[numModes];
+        for (int i = minFps; i <= maxFps; i++) {
+            modes[i - minFps] = new Display.Mode(
+                    /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i);
+        }
+        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>();
+        supportedModesByDisplay.put(displayId, modes);
+        director.injectSupportedModesByDisplay(supportedModesByDisplay);
+        SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>();
+        defaultModesByDisplay.put(displayId, modes[0]);
+        director.injectDefaultModeByDisplay(defaultModesByDisplay);
+        return director;
+    }
+
+    private int[] intRange(int min, int max) {
+        int[] range = new int[max - min + 1];
+        for (int i = min; i <= max; i++) {
+            range[i - min] = i;
+        }
+        return range;
+    }
+
+    @Test
+    public void testDisplayModeVoting() {
+        int displayId = 0;
+
+        // With no votes present, DisplayModeDirector should allow any refresh rate.
+        assertEquals(new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/60,
+                             new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY),
+                             intRange(60, 90)),
+                createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayConfigSpecs(
+                        displayId));
+
+        int numPriorities =
+                DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
+
+        // Ensure vote priority works as expected. As we add new votes with higher priority, they
+        // should take precedence over lower priority votes.
+        {
+            int minFps = 60;
+            int maxFps = 90;
+            DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+            assertTrue(2 * numPriorities < maxFps - minFps + 1);
+            SparseArray<DisplayModeDirector.Vote> votes =
+                    new SparseArray<DisplayModeDirector.Vote>();
+            SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
+                    new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+            votesByDisplay.put(displayId, votes);
+            for (int i = 0; i < numPriorities; i++) {
+                int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i;
+                votes.put(
+                        priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i));
+                director.injectVotesByDisplay(votesByDisplay);
+                assertEquals(
+                        new DisplayModeDirector.DesiredDisplayConfigSpecs(
+                                /*defaultModeId=*/minFps + i,
+                                new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i),
+                                intRange(minFps + i, maxFps - i)),
+                        director.getDesiredDisplayConfigSpecs(displayId));
+            }
+        }
+
+        // Ensure lower priority votes are able to influence the final decision, even in the
+        // presence of higher priority votes.
+        {
+            assertTrue(numPriorities >= 2);
+            DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+            SparseArray<DisplayModeDirector.Vote> votes =
+                    new SparseArray<DisplayModeDirector.Vote>();
+            SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
+                    new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+            votesByDisplay.put(displayId, votes);
+            votes.put(DisplayModeDirector.Vote.MAX_PRIORITY,
+                    DisplayModeDirector.Vote.forRefreshRates(65, 85));
+            votes.put(DisplayModeDirector.Vote.MIN_PRIORITY,
+                    DisplayModeDirector.Vote.forRefreshRates(70, 80));
+            director.injectVotesByDisplay(votesByDisplay);
+            assertEquals(
+                    new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/70,
+                            new DisplayModeDirector.RefreshRateRange(70, 80), intRange(70, 80)),
+                    director.getDesiredDisplayConfigSpecs(displayId));
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
index 495923d..a14197b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
@@ -30,6 +30,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -109,7 +110,7 @@
                                                 /* isHashedValue= */ false))),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -154,7 +155,7 @@
                                                 "test_cert",
                                                 /* isHashedValue= */ false))),
                         Rule.DENY);
-        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -200,7 +201,7 @@
                                                 /* isHashedValue= */ false))),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -237,7 +238,7 @@
                                                 /* isHashedValue= */ false))),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -273,7 +274,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
-                () -> xmlParser.parse(ruleXmlCompoundFormula));
+                () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -302,7 +303,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "For input string: \"INVALID_OPERATOR\"",
-                () -> xmlParser.parse(ruleXmlCompoundFormula));
+                () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -330,7 +331,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "For input string: \"INVALID_EFFECT\"",
-                () -> xmlParser.parse(ruleXmlCompoundFormula));
+                () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -360,7 +361,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Found unexpected tag: InvalidAtomicFormula",
-                () -> xmlParser.parse(ruleXmlCompoundFormula));
+                () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -387,7 +388,7 @@
                                 /* isHashedValue= */ false),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -415,7 +416,7 @@
                                 AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -441,7 +442,7 @@
                         new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -470,7 +471,7 @@
                                 /* isHashedValue= */ false),
                         Rule.DENY);
 
-        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
 
         assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
     }
@@ -495,7 +496,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Found unexpected key: -1",
-                () -> xmlParser.parse(ruleXmlAtomicFormula));
+                () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -517,7 +518,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Unknown effect: -1",
-                () -> xmlParser.parse(ruleXmlAtomicFormula));
+                () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -545,7 +546,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Unknown connector: -1",
-                () -> xmlParser.parse(ruleXmlCompoundFormula));
+                () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -569,7 +570,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "For input string: \"com.app.test\"",
-                () -> xmlParser.parse(ruleXmlAtomicFormula));
+                () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
@@ -595,7 +596,7 @@
         assertExpectException(
                 RuleParseException.class,
                 /* expectedExceptionMessageRegex */ "Rules must start with RuleList <RL> tag",
-                () -> xmlParser.parse(ruleXmlWithNoRuleList));
+                () -> xmlParser.parse(ruleXmlWithNoRuleList.getBytes(StandardCharsets.UTF_8)));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
new file mode 100644
index 0000000..4b7dd36
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.server.pm;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageParser;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.compat.PlatformCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+
+/**
+ * {@link SELinuxMMAC} tests.
+ */
+@RunWith(MockitoJUnitRunner.class)
+@Presubmit
+public class SELinuxMMACTest {
+
+    private static final String PACKAGE_NAME = "my.package";
+    private static final int OPT_IN_VERSION = android.os.Build.VERSION_CODES.R;
+
+    @Mock
+    PlatformCompat mMockCompatibility;
+
+    PackageParser.Package mPkg;
+
+    @Before
+    public void setUp() {
+        mPkg = new PackageParser.Package(PACKAGE_NAME);
+        mPkg.applicationInfo.targetSdkVersion = 28;
+    }
+
+    @Test
+    public void getSeInfoOptInToLatest() {
+        when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+                mPkg.applicationInfo)).thenReturn(true);
+        assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+                is("default:targetSdkVersion=" + OPT_IN_VERSION));
+    }
+
+    @Test
+    public void getSeInfoNoOptIn() {
+        when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+                mPkg.applicationInfo)).thenReturn(false);
+        assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+                is("default:targetSdkVersion=28"));
+    }
+
+    @Test
+    public void getSeInfoNoOptInButAlreadyR() {
+        mPkg.applicationInfo.targetSdkVersion = OPT_IN_VERSION;
+        when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+                mPkg.applicationInfo)).thenReturn(false);
+        assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+                is("default:targetSdkVersion=" + OPT_IN_VERSION));
+    }
+}
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 3ea3b3c..74ef034 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -48,6 +48,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
 
+import com.android.server.compat.PlatformCompat;
+
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -71,12 +73,15 @@
     @Mock
     UserManagerInternal mMockUserManager;
     @Mock
+    PlatformCompat mMockCompatibility;
+    @Mock
     PackageManagerService.Injector mMockInjector;
 
     @Before
     public void setupInjector() {
         when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper);
         when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager);
+        when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility);
     }
 
     @Before
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index d071927..313afbb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -703,6 +703,21 @@
         assertEquals(canBeCreatedCount, created.get());
     }
 
+    @MediumTest
+    public void testGetUserHandles_createNewUser_shouldFindNewUser() {
+        UserInfo user = createUser("Guest 1", UserManager.USER_TYPE_FULL_GUEST, /*flags*/ 0);
+
+        boolean found = false;
+        List<UserHandle> userHandles = mUserManager.getUserHandles(/* excludeDying= */ true);
+        for (UserHandle userHandle: userHandles) {
+            if (userHandle.getIdentifier() == user.id) {
+                found = true;
+            }
+        }
+
+        assertTrue(found);
+    }
+
     private boolean isPackageInstalledForUser(String packageName, int userId) {
         try {
             return mPackageManager.getPackageInfoAsUser(packageName, 0, userId) != null;
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 601e2e9..790f2b4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -24,6 +24,7 @@
 import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT;
 import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE;
 import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA;
 import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST;
 import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_LOG;
 
@@ -394,33 +395,45 @@
         assertFalse(mUserSystemPackageInstaller.isLogMode());
         assertFalse(mUserSystemPackageInstaller.isEnforceMode());
         assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
 
         setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
         assertTrue(mUserSystemPackageInstaller.isLogMode());
         assertFalse(mUserSystemPackageInstaller.isEnforceMode());
         assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
 
         setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
         assertFalse(mUserSystemPackageInstaller.isLogMode());
         assertTrue(mUserSystemPackageInstaller.isEnforceMode());
         assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
 
         setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
         assertFalse(mUserSystemPackageInstaller.isLogMode());
         assertFalse(mUserSystemPackageInstaller.isEnforceMode());
         assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
+
+        setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA);
+        assertFalse(mUserSystemPackageInstaller.isLogMode());
+        assertFalse(mUserSystemPackageInstaller.isEnforceMode());
+        assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertTrue(mUserSystemPackageInstaller.isIgnoreOtaMode());
 
         setUserTypePackageWhitelistMode(
                 USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
         assertTrue(mUserSystemPackageInstaller.isLogMode());
         assertTrue(mUserSystemPackageInstaller.isEnforceMode());
         assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
 
         setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
                 | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
         assertFalse(mUserSystemPackageInstaller.isLogMode());
         assertTrue(mUserSystemPackageInstaller.isEnforceMode());
         assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+        assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
     }
 
     /** Sets the whitelist mode to the desired value via adb's setprop. */
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 4476918..642cedb 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -17,6 +17,12 @@
 package com.android.server.power;
 
 import static android.os.BatteryStats.Uid.NUM_USER_ACTIVITY_TYPES;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import static com.android.server.power.AttentionDetector.DEFAULT_POST_DIM_CHECK_DURATION_MILLIS;
+import static com.android.server.power.AttentionDetector.DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS;
+import static com.android.server.power.AttentionDetector.KEY_POST_DIM_CHECK_DURATION_MILLIS;
+import static com.android.server.power.AttentionDetector.KEY_PRE_DIM_CHECK_DURATION_MILLIS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -37,6 +43,7 @@
 import android.os.PowerManagerInternal;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.attention.AttentionService;
 import android.test.AndroidTestCase;
@@ -53,6 +60,7 @@
 
 @SmallTest
 public class AttentionDetectorTest extends AndroidTestCase {
+    private static final long DEFAULT_DIM_DURATION_MILLIS = 6_000L;
 
     @Mock
     private PackageManager mPackageManager;
@@ -63,7 +71,8 @@
     @Mock
     private Runnable mOnUserAttention;
     private TestableAttentionDetector mAttentionDetector;
-    private long mAttentionTimeout;
+    private AttentionDetector mRealAttentionDetector;
+    private long mPreDimCheckDuration;
     private long mNextDimming;
     private int mIsSettingEnabled;
 
@@ -77,6 +86,7 @@
                 .thenReturn(true);
         when(mWindowManagerInternal.isKeyguardShowingAndNotOccluded()).thenReturn(false);
         mAttentionDetector = new TestableAttentionDetector();
+        mRealAttentionDetector = new AttentionDetector(mOnUserAttention, new Object());
         mAttentionDetector.onWakefulnessChangeStarted(PowerManagerInternal.WAKEFULNESS_AWAKE);
         mAttentionDetector.setAttentionServiceSupported(true);
         mNextDimming = SystemClock.uptimeMillis() + 3000L;
@@ -94,6 +104,13 @@
     public void tearDown() {
         Settings.System.putIntForUser(getContext().getContentResolver(),
                 Settings.System.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT);
+
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS,
+                Long.toString(DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS), false);
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS,
+                Long.toString(DEFAULT_POST_DIM_CHECK_DURATION_MILLIS), false);
     }
 
     @Test
@@ -175,7 +192,7 @@
         long now = SystemClock.uptimeMillis();
         mNextDimming = now;
         mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
-        mAttentionDetector.updateUserActivity(mNextDimming + 5000L);
+        mAttentionDetector.updateUserActivity(mNextDimming + 5000L, DEFAULT_DIM_DURATION_MILLIS);
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
     }
 
@@ -184,7 +201,8 @@
         long now = SystemClock.uptimeMillis();
         mNextDimming = now;
         mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
-        long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L);
+        long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L,
+                DEFAULT_DIM_DURATION_MILLIS);
         assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L);
     }
 
@@ -192,7 +210,7 @@
     public void testOnUserActivity_ignoresAfterMaximumExtension() {
         long now = SystemClock.uptimeMillis();
         mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
-        mAttentionDetector.updateUserActivity(now + 2000L);
+        mAttentionDetector.updateUserActivity(now + 2000L, DEFAULT_DIM_DURATION_MILLIS);
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
     }
 
@@ -203,7 +221,8 @@
         assertThat(when).isLessThan(mNextDimming);
         clearInvocations(mAttentionManagerInternal);
 
-        long redundantWhen = mAttentionDetector.updateUserActivity(mNextDimming);
+        long redundantWhen = mAttentionDetector.updateUserActivity(mNextDimming,
+                DEFAULT_DIM_DURATION_MILLIS);
         assertThat(redundantWhen).isEqualTo(mNextDimming);
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
     }
@@ -212,7 +231,8 @@
     public void testOnUserActivity_skipsIfAlreadyScheduled() {
         registerAttention();
         reset(mAttentionManagerInternal);
-        long when = mAttentionDetector.updateUserActivity(mNextDimming + 1);
+        long when = mAttentionDetector.updateUserActivity(mNextDimming + 1,
+                DEFAULT_DIM_DURATION_MILLIS);
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
         assertThat(when).isLessThan(mNextDimming);
     }
@@ -300,11 +320,95 @@
         verify(mOnUserAttention, never()).run();
     }
 
+    @Test
+    public void testGetPreDimCheckDurationMillis_handlesGoodFlagValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS, "555", false);
+        assertThat(mRealAttentionDetector.getPreDimCheckDurationMillis()).isEqualTo(555);
+    }
+
+    @Test
+    public void testGetPreDimCheckDurationMillis_rejectsNegativeValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS, "-50", false);
+        assertThat(mRealAttentionDetector.getPreDimCheckDurationMillis()).isEqualTo(
+                DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
+    }
+
+    @Test
+    public void testGetPreDimCheckDurationMillis_rejectsTooBigValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS, "20000", false);
+        assertThat(mRealAttentionDetector.getPreDimCheckDurationMillis()).isEqualTo(
+                DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
+    }
+
+    @Test
+    public void testGetPreDimCheckDurationMillis_handlesBadFlagValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS, "20000k", false);
+        assertThat(mRealAttentionDetector.getPreDimCheckDurationMillis()).isEqualTo(
+                DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
+
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_PRE_DIM_CHECK_DURATION_MILLIS, "0.25", false);
+        assertThat(mRealAttentionDetector.getPreDimCheckDurationMillis()).isEqualTo(
+                DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
+    }
+
+    @Test
+    public void testGetPostDimCheckDurationMillis_handlesGoodFlagValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "333", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(
+                DEFAULT_DIM_DURATION_MILLIS)).isEqualTo(333);
+    }
+
+    @Test
+    public void testGetPostDimCheckDurationMillis_capsGoodFlagValueByMaxDimDuration() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "7000", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(6500)).isEqualTo(6500);
+    }
+
+    @Test
+    public void testGetPostDimCheckDurationMillis_rejectsNegativeValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "-50", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(
+                DEFAULT_DIM_DURATION_MILLIS)).isEqualTo(
+                DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
+    }
+
+    @Test
+    public void testGetPostDimCheckDurationMillis_rejectsTooBigValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "20000", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(
+                DEFAULT_DIM_DURATION_MILLIS)).isEqualTo(
+                DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
+    }
+
+    @Test
+    public void testGetPostDimCheckDurationMillis_handlesBadFlagValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "20000k", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(
+                DEFAULT_DIM_DURATION_MILLIS)).isEqualTo(
+                DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
+
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_POST_DIM_CHECK_DURATION_MILLIS, "0.25", false);
+        assertThat(mRealAttentionDetector.getPostDimCheckDurationMillis(
+                DEFAULT_DIM_DURATION_MILLIS)).isEqualTo(
+                DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
+    }
+
     private long registerAttention() {
-        mAttentionTimeout = 4000L;
+        mPreDimCheckDuration = 4000L;
         mAttentionDetector.onUserActivity(SystemClock.uptimeMillis(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH);
-        return mAttentionDetector.updateUserActivity(mNextDimming);
+        return mAttentionDetector.updateUserActivity(mNextDimming, DEFAULT_DIM_DURATION_MILLIS);
     }
 
     private class TestableAttentionDetector extends AttentionDetector {
@@ -329,8 +433,8 @@
         }
 
         @Override
-        public long getAttentionTimeout() {
-            return mAttentionTimeout;
+        public long getPreDimCheckDurationMillis() {
+            return mPreDimCheckDuration;
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
index 317fd4d..8a7edf7 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
@@ -40,7 +40,7 @@
 import java.time.Duration;
 
 @RunWith(AndroidJUnit4.class)
-public class SimpleTimeZoneDetectorStrategyTest {
+public class SimpleTimeDetectorStrategyTest {
 
     private static final Scenario SCENARIO_1 = new Scenario.Builder()
             .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
@@ -440,7 +440,7 @@
             mSystemClockMillis = systemClockMillis;
         }
 
-        public void pokeTimeDetectionEnabled(boolean enabled) {
+        public void pokeAutoTimeDetectionEnabled(boolean enabled) {
             mTimeDetectionEnabled = enabled;
         }
 
@@ -457,6 +457,10 @@
             mSystemClockMillis += incrementMillis;
         }
 
+        public void simulateAutoTimeZoneDetectionToggle() {
+            mTimeDetectionEnabled = !mTimeDetectionEnabled;
+        }
+
         public void verifySystemClockNotSet() {
             assertFalse(mSystemClockWasSet);
         }
@@ -493,7 +497,7 @@
         private final FakeCallback mFakeCallback;
         private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
 
-        public Script() {
+        Script() {
             mFakeCallback = new FakeCallback();
             mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
             mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
@@ -501,7 +505,7 @@
         }
 
         Script pokeTimeDetectionEnabled(boolean enabled) {
-            mFakeCallback.pokeTimeDetectionEnabled(enabled);
+            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
             return this;
         }
 
@@ -535,9 +539,8 @@
         }
 
         Script simulateAutoTimeDetectionToggle() {
-            boolean enabled = !mFakeCallback.isAutoTimeDetectionEnabled();
-            mFakeCallback.pokeTimeDetectionEnabled(enabled);
-            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled);
+            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionChanged();
             return this;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 4efe771..9951e85 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -17,13 +17,11 @@
 package com.android.server.timedetector;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,12 +30,17 @@
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
 import android.util.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.timedetector.TimeDetectorStrategy.Callback;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,54 +55,62 @@
     private Callback mMockCallback;
 
     private TimeDetectorService mTimeDetectorService;
+    private HandlerThread mHandlerThread;
+    private TestHandler mTestHandler;
+
 
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
+
+        // Create a thread + handler for processing the work that the service posts.
+        mHandlerThread = new HandlerThread("TimeDetectorServiceTest");
+        mHandlerThread.start();
+        mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
         mMockCallback = mock(Callback.class);
         mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
 
         mTimeDetectorService = new TimeDetectorService(
-                mMockContext, mMockCallback,
+                mMockContext, mTestHandler, mMockCallback,
                 mStubbedTimeDetectorStrategy);
     }
 
-    @Test(expected=SecurityException.class)
-    public void testStubbedCall_withoutPermission() {
-        doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingPermission(anyString(), any());
-        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
-
-        try {
-            mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
-        } finally {
-            verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.SET_TIME), anyString());
-        }
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        mHandlerThread.join();
     }
 
     @Test
-    public void testSuggestPhoneTime() {
+    public void testSuggestPhoneTime() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
         PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
         mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
-
-        verify(mMockContext)
-                .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
-        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
-    }
-
-    @Test
-    public void testSuggestManualTime() {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
-        mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.SET_TIME),
                 anyString());
+
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
+    }
+
+    @Test
+    public void testSuggestManualTime() throws Exception {
+        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
+        mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingPermission(
+                eq(android.Manifest.permission.SET_TIME),
+                anyString());
+
+        mTestHandler.waitForEmptyQueue();
         mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
     }
 
@@ -115,18 +126,16 @@
     }
 
     @Test
-    public void testAutoTimeDetectionToggle() {
-        when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(true);
+    public void testAutoTimeDetectionToggle() throws Exception {
+        mTimeDetectorService.handleAutoTimeDetectionToggle();
+        mTestHandler.assertTotalMessagesEnqueued(1);
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
 
         mTimeDetectorService.handleAutoTimeDetectionToggle();
-
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
-
-        when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(false);
-
-        mTimeDetectorService.handleAutoTimeDetectionToggle();
-
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
+        mTestHandler.assertTotalMessagesEnqueued(2);
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
     }
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
@@ -147,7 +156,7 @@
         // Call tracking.
         private PhoneTimeSuggestion mLastPhoneSuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
-        private Boolean mLastAutoTimeDetectionToggle;
+        private boolean mLastAutoTimeDetectionToggleCalled;
         private boolean mDumpCalled;
 
         @Override
@@ -167,9 +176,9 @@
         }
 
         @Override
-        public void handleAutoTimeDetectionToggle(boolean enabled) {
+        public void handleAutoTimeDetectionChanged() {
             resetCallTracking();
-            mLastAutoTimeDetectionToggle = enabled;
+            mLastAutoTimeDetectionToggleCalled = true;
         }
 
         @Override
@@ -181,7 +190,7 @@
         void resetCallTracking() {
             mLastPhoneSuggestion = null;
             mLastManualSuggestion = null;
-            mLastAutoTimeDetectionToggle = null;
+            mLastAutoTimeDetectionToggleCalled = false;
             mDumpCalled = false;
         }
 
@@ -193,13 +202,45 @@
             assertEquals(expectedSuggestion, mLastManualSuggestion);
         }
 
-        void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
-            assertNotNull(mLastAutoTimeDetectionToggle);
-            assertEquals(expectedEnable, mLastAutoTimeDetectionToggle);
+        void verifyHandleAutoTimeDetectionToggleCalled() {
+            assertTrue(mLastAutoTimeDetectionToggleCalled);
         }
 
         void verifyDumpCalled() {
             assertTrue(mDumpCalled);
         }
     }
+
+    /**
+     * A Handler that can track posts/sends and wait for work to be completed.
+     */
+    private static class TestHandler extends Handler {
+
+        private int mMessagesSent;
+
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            mMessagesSent++;
+            return super.sendMessageAtTime(msg, uptimeMillis);
+        }
+
+        /** Asserts the number of messages posted or sent is as expected. */
+        void assertTotalMessagesEnqueued(int expected) {
+            assertEquals(expected, mMessagesSent);
+        }
+
+        /**
+         * Waits for all currently enqueued work due to be processed to be completed before
+         * returning.
+         */
+        void waitForEmptyQueue() throws InterruptedException {
+            while (!getLooper().getQueue().isIdle()) {
+                Thread.sleep(100);
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
index f9f23c3..270436d 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
@@ -24,18 +24,19 @@
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_USAGE_THRESHOLD;
 
 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 android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
@@ -49,6 +50,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
+import java.util.Objects;
 
 /**
  * White-box unit tests for {@link TimeZoneDetectorStrategy}.
@@ -64,16 +66,17 @@
     // than the previous.
     private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW),
+                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
-                    SCORE_MEDIUM),
+                    PHONE_SCORE_MEDIUM),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_MEDIUM),
-            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, SCORE_HIGH),
-            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_MEDIUM),
+            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGH),
+            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+                    PHONE_SCORE_HIGH),
             newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_HIGHEST),
-            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, SCORE_HIGHEST),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_HIGHEST),
+            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
     };
 
     private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
@@ -87,11 +90,11 @@
     }
 
     @Test
-    public void testEmptySuggestions() {
+    public void testEmptyPhoneSuggestions() {
         PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion();
         PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion();
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.suggestPhoneTimeZone(phone1TimeZoneSuggestion)
@@ -99,38 +102,38 @@
 
         // Assert internal service state.
         QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, PHONE_SCORE_NONE);
         assertEquals(expectedPhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertNull(mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
         assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
         script.suggestPhoneTimeZone(phone2TimeZoneSuggestion)
                 .verifyTimeZoneNotSet();
 
         // Assert internal service state.
         QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, PHONE_SCORE_NONE);
         assertEquals(expectedPhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedPhone2ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
         // Phone 1 should always beat phone 2, all other things being equal.
         assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     @Test
-    public void testFirstPlausibleSuggestionAcceptedWhenTimeZoneUninitialized() {
+    public void testFirstPlausiblePhoneSuggestionAcceptedWhenTimeZoneUninitialized() {
         SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW);
+                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW);
         PhoneTimeZoneSuggestion lowQualitySuggestion =
                 testCase.createSuggestion(PHONE1_ID, "America/New_York");
 
         // The device time zone setting is left uninitialized.
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true);
+                .initializeAutoTimeZoneDetection(true);
 
         // The very first suggestion will be taken.
         script.suggestPhoneTimeZone(lowQualitySuggestion)
@@ -142,7 +145,7 @@
         assertEquals(expectedScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
         // Another low quality suggestion will be ignored now that the setting is initialized.
         PhoneTimeZoneSuggestion lowQualitySuggestion2 =
@@ -156,7 +159,7 @@
         assertEquals(expectedScoredSuggestion2,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     /**
@@ -164,12 +167,12 @@
      * the strategy is "opinionated".
      */
     @Test
-    public void testTogglingTimeZoneDetection() {
+    public void testTogglingAutoTimeZoneDetection() {
         Script script = new Script();
 
         for (SuggestionTestCase testCase : TEST_CASES) {
             // Start with the device in a known state.
-            script.initializeTimeZoneDetectionEnabled(false)
+            script.initializeAutoTimeZoneDetection(false)
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
             PhoneTimeZoneSuggestion suggestion =
@@ -186,14 +189,14 @@
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
-            script.timeZoneDetectionEnabled(true);
+            script.autoTimeZoneDetectionEnabled(true);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -203,24 +206,24 @@
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
-            script.timeZoneDetectionEnabled(false)
+            script.autoTimeZoneDetectionEnabled(false)
                     .verifyTimeZoneNotSet();
 
             // Assert internal service state.
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
         }
     }
 
     @Test
-    public void testSuggestionsSinglePhone() {
+    public void testPhoneSuggestionsSinglePhone() {
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (SuggestionTestCase testCase : TEST_CASES) {
@@ -254,7 +257,7 @@
                 new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore);
 
         script.suggestPhoneTimeZone(zonePhone1Suggestion);
-        if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+        if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
             script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
         } else {
             script.verifyTimeZoneNotSet();
@@ -264,7 +267,7 @@
         assertEquals(expectedZonePhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedZonePhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     /**
@@ -278,12 +281,12 @@
         PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion();
         PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion();
         QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, PHONE_SCORE_NONE);
         QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, PHONE_SCORE_NONE);
 
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
                 // Initialize the latest suggestions as empty so we don't need to worry about nulls
                 // below for the first loop.
@@ -305,7 +308,7 @@
 
             // Start the test by making a suggestion for phone 1.
             script.suggestPhoneTimeZone(zonePhone1Suggestion);
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -317,7 +320,7 @@
             assertEquals(expectedEmptyPhone2ScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Phone 2 then makes an alternative suggestion with an identical score. Phone 1's
             // suggestion should still "win" if it is above the required threshold.
@@ -331,13 +334,13 @@
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             // Phone 1 should always beat phone 2, all other things being equal.
             assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the
             // zoneId is different, the time zone setting should be updated if the score is high
             // enough.
             script.suggestPhoneTimeZone(emptyPhone1Suggestion);
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(zonePhone2Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -349,7 +352,7 @@
             assertEquals(expectedZonePhone2ScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             assertEquals(expectedZonePhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Reset the state for the next loop.
             script.suggestPhoneTimeZone(emptyPhone2Suggestion)
@@ -369,10 +372,11 @@
     @Test
     public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true);
+                .initializeAutoTimeZoneDetection(true);
 
         SuggestionTestCase testCase =
-                newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH);
+                newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+                        PHONE_SCORE_HIGH);
         PhoneTimeZoneSuggestion losAngelesSuggestion =
                 testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
         PhoneTimeZoneSuggestion newYorkSuggestion =
@@ -387,21 +391,49 @@
 
         // Toggling time zone detection should set the device time zone only if the current setting
         // value is different from the most recent phone suggestion.
-        script.timeZoneDetectionEnabled(false)
+        script.autoTimeZoneDetectionEnabled(false)
                 .verifyTimeZoneNotSet()
-                .timeZoneDetectionEnabled(true)
+                .autoTimeZoneDetectionEnabled(true)
                 .verifyTimeZoneNotSet();
 
         // Simulate a user turning auto detection off, a new suggestion being made while auto
         // detection is off, and the user turning it on again.
-        script.timeZoneDetectionEnabled(false)
+        script.autoTimeZoneDetectionEnabled(false)
                 .suggestPhoneTimeZone(newYorkSuggestion)
                 .verifyTimeZoneNotSet();
         // Latest suggestion should be used.
-        script.timeZoneDetectionEnabled(true)
+        script.autoTimeZoneDetectionEnabled(true)
                 .verifyTimeZoneSetAndReset(newYorkSuggestion);
     }
 
+    @Test
+    public void testManualSuggestion_autoTimeZoneDetectionEnabled() {
+        Script script = new Script()
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .initializeAutoTimeZoneDetection(true);
+
+        // Auto time zone detection is enabled so the manual suggestion should be ignored.
+        script.suggestManualTimeZone(createManualSuggestion("Europe/Paris"))
+            .verifyTimeZoneNotSet();
+    }
+
+
+    @Test
+    public void testManualSuggestion_autoTimeZoneDetectionDisabled() {
+        Script script = new Script()
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .initializeAutoTimeZoneDetection(false);
+
+        // Auto time zone detection is disabled so the manual suggestion should be used.
+        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
+        script.suggestManualTimeZone(manualSuggestion)
+            .verifyTimeZoneSetAndReset(manualSuggestion);
+    }
+
+    private ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
+        return new ManualTimeZoneSuggestion(zoneId);
+    }
+
     private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() {
         return new PhoneTimeZoneSuggestion.Builder(PHONE1_ID).build();
     }
@@ -410,55 +442,86 @@
         return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
     }
 
-    class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+    static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
 
-        private boolean mTimeZoneDetectionEnabled;
-        private TestState<String> mTimeZoneId = new TestState<>();
+        private boolean mAutoTimeZoneDetectionEnabled;
+        private TestState<TimeZoneChange> mTimeZoneChanges = new TestState<>();
+        private String mTimeZoneId;
 
         @Override
-        public boolean isTimeZoneDetectionEnabled() {
-            return mTimeZoneDetectionEnabled;
+        public boolean isAutoTimeZoneDetectionEnabled() {
+            return mAutoTimeZoneDetectionEnabled;
         }
 
         @Override
         public boolean isDeviceTimeZoneInitialized() {
-            return mTimeZoneId.getLatest() != null;
+            return mTimeZoneId != null;
         }
 
         @Override
         public String getDeviceTimeZone() {
-            return mTimeZoneId.getLatest();
+            return mTimeZoneId;
         }
 
         @Override
-        public void setDeviceTimeZone(String zoneId) {
-            mTimeZoneId.set(zoneId);
+        public void setDeviceTimeZone(String zoneId, boolean withNetworkBroadcast) {
+            mTimeZoneId = zoneId;
+            mTimeZoneChanges.set(new TimeZoneChange(zoneId, withNetworkBroadcast));
         }
 
-        void initializeTimeZoneDetectionEnabled(boolean enabled) {
-            mTimeZoneDetectionEnabled = enabled;
+        void initializeAutoTimeZoneDetection(boolean enabled) {
+            mAutoTimeZoneDetectionEnabled = enabled;
         }
 
         void initializeTimeZone(String zoneId) {
-            mTimeZoneId.init(zoneId);
+            mTimeZoneId = zoneId;
         }
 
-        void setTimeZoneDetectionEnabled(boolean enabled) {
-            mTimeZoneDetectionEnabled = enabled;
+        void setAutoTimeZoneDetectionEnabled(boolean enabled) {
+            mAutoTimeZoneDetectionEnabled = enabled;
         }
 
         void assertTimeZoneNotSet() {
-            mTimeZoneId.assertHasNotBeenSet();
+            mTimeZoneChanges.assertHasNotBeenSet();
         }
 
-        void assertTimeZoneSet(String timeZoneId) {
-            mTimeZoneId.assertHasBeenSet();
-            mTimeZoneId.assertChangeCount(1);
-            mTimeZoneId.assertLatestEquals(timeZoneId);
+        void assertTimeZoneSet(String timeZoneId, boolean withNetworkBroadcast) {
+            mTimeZoneChanges.assertHasBeenSet();
+            mTimeZoneChanges.assertChangeCount(1);
+            TimeZoneChange expectedChange = new TimeZoneChange(timeZoneId, withNetworkBroadcast);
+            mTimeZoneChanges.assertLatestEquals(expectedChange);
         }
 
         void commitAllChanges() {
-            mTimeZoneId.commitLatest();
+            mTimeZoneChanges.commitLatest();
+        }
+    }
+
+    private static class TimeZoneChange {
+        private final String mTimeZoneId;
+        private final boolean mWithNetworkBroadcast;
+
+        private TimeZoneChange(String timeZoneId, boolean withNetworkBroadcast) {
+            mTimeZoneId = timeZoneId;
+            mWithNetworkBroadcast = withNetworkBroadcast;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TimeZoneChange that = (TimeZoneChange) o;
+            return mWithNetworkBroadcast == that.mWithNetworkBroadcast
+                    && mTimeZoneId.equals(that.mTimeZoneId);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTimeZoneId, mWithNetworkBroadcast);
         }
     }
 
@@ -517,8 +580,8 @@
      */
     private class Script {
 
-        Script initializeTimeZoneDetectionEnabled(boolean enabled) {
-            mFakeTimeZoneDetectorStrategyCallback.initializeTimeZoneDetectionEnabled(enabled);
+        Script initializeAutoTimeZoneDetection(boolean enabled) {
+            mFakeTimeZoneDetectorStrategyCallback.initializeAutoTimeZoneDetection(enabled);
             return this;
         }
 
@@ -527,24 +590,21 @@
             return this;
         }
 
-        Script timeZoneDetectionEnabled(boolean enabled) {
-            mFakeTimeZoneDetectorStrategyCallback.setTimeZoneDetectionEnabled(enabled);
-            mTimeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+        Script autoTimeZoneDetectionEnabled(boolean enabled) {
+            mFakeTimeZoneDetectorStrategyCallback.setAutoTimeZoneDetectionEnabled(enabled);
+            mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
             return this;
         }
 
-        /** Simulates the time zone detection service receiving a phone-originated suggestion. */
+        /** Simulates the time zone detection strategy receiving a phone-originated suggestion. */
         Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) {
             mTimeZoneDetectorStrategy.suggestPhoneTimeZone(phoneTimeZoneSuggestion);
             return this;
         }
 
-        /** Simulates the user manually setting the time zone. */
-        Script manuallySetTimeZone(String timeZoneId) {
-            // Assert the test code is correct to call this method.
-            assertFalse(mFakeTimeZoneDetectorStrategyCallback.isTimeZoneDetectionEnabled());
-
-            mFakeTimeZoneDetectorStrategyCallback.initializeTimeZone(timeZoneId);
+        /** Simulates the time zone detection strategy receiving a user-originated suggestion. */
+        Script suggestManualTimeZone(ManualTimeZoneSuggestion manualTimeZoneSuggestion) {
+            mTimeZoneDetectorStrategy.suggestManualTimeZone(manualTimeZoneSuggestion);
             return this;
         }
 
@@ -553,8 +613,22 @@
             return this;
         }
 
-        Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
-            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(timeZoneSuggestion.getZoneId());
+        Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion suggestion) {
+            // Phone suggestions should cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
+            // broadcast.
+            boolean withNetworkBroadcast = true;
+            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
+                    suggestion.getZoneId(), withNetworkBroadcast);
+            mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
+            return this;
+        }
+
+        Script verifyTimeZoneSetAndReset(ManualTimeZoneSuggestion suggestion) {
+            // Manual suggestions should not cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
+            // broadcast.
+            boolean withNetworkBroadcast = false;
+            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
+                    suggestion.getZoneId(), withNetworkBroadcast);
             mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
             return this;
         }
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
index 909e9bb..201cd05 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
@@ -18,8 +18,7 @@
         package="com.android.servicestests.apps.conntestapp">
 
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
 
     <application>
         <activity android:name=".ConnTestActivity"
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 734761f..12074dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.content.ComponentName.createRelative;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -34,7 +35,7 @@
 import android.content.Intent;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.util.SparseIntArray;
+import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
 
@@ -58,11 +59,13 @@
 @Presubmit
 public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
     private ActivityMetricsLogger mActivityMetricsLogger;
+    private ActivityMetricsLogger.LaunchingState mLaunchingState;
     private ActivityMetricsLaunchObserver mLaunchObserver;
     private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
 
     private ActivityRecord mTrampolineActivity;
     private ActivityRecord mTopActivity;
+    private boolean mLaunchTopByTrampoline;
 
     @Before
     public void setUpAMLO() {
@@ -76,9 +79,13 @@
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mTrampolineActivity = new ActivityBuilder(mService).setCreateTask(true).build();
+        mTrampolineActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TrampolineActivity"))
+                .build();
         mTopActivity = new ActivityBuilder(mService)
                 .setTask(mTrampolineActivity.getTask())
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
                 .build();
     }
 
@@ -114,42 +121,42 @@
         return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
     }
 
-    private void onIntentStarted() {
-        Intent intent = new Intent("action 1");
+    private void onIntentStarted(Intent intent) {
+        notifyActivityLaunching(intent);
 
-        mActivityMetricsLogger.notifyActivityLaunching(intent);
-
-        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+        // If it is launching top activity from trampoline activity, the observer shouldn't receive
+        // onActivityLaunched because the activities should belong to the same transition.
+        if (!mLaunchTopByTrampoline) {
+            verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+        }
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
     @Test
     public void testOnIntentFailed() {
-        onIntentStarted();
+        onIntentStarted(new Intent("testOnIntentFailed"));
 
         // Bringing an intent that's already running 'to front' is not considered
         // as an ACTIVITY_LAUNCHED state transition.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
-                null /* launchedActivity */);
+        notifyActivityLaunched(START_TASK_TO_FRONT, null /* launchedActivity */);
 
         verifyAsync(mLaunchObserver).onIntentFailed();
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
-    private void onActivityLaunched() {
-        onIntentStarted();
+    private void onActivityLaunched(ActivityRecord activity) {
+        onIntentStarted(activity.intent);
+        notifyActivityLaunched(START_SUCCESS, activity);
 
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
-
-        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
+        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(activity), anyInt());
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
     @Test
     public void testOnActivityLaunchFinished() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         notifyWindowsDrawn(mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -158,12 +165,12 @@
 
     @Test
     public void testOnActivityLaunchCancelled_hasDrawn() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
         mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
 
         // Cannot time already-visible activities.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+        notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
         verifyNoMoreInteractions(mLaunchObserver);
@@ -182,8 +189,8 @@
                 .build();
         mSupervisor.readyToResume();
 
-        mActivityMetricsLogger.notifyActivityLaunching(noDrawnActivity.intent);
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
+        notifyActivityLaunching(noDrawnActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
 
         noDrawnActivity.destroyIfPossible("test");
         mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
@@ -193,12 +200,12 @@
 
     @Test
     public void testOnReportFullyDrawn() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
         // The activity reports fully drawn before windows drawn, then the fully drawn event will
         // be pending (see {@link WindowingModeTransitionInfo#pendingFullyDrawn}).
         mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false);
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         // The pending fully drawn event should send when the actual windows drawn event occurs.
         notifyWindowsDrawn(mTopActivity);
 
@@ -208,35 +215,56 @@
     }
 
     private void onActivityLaunchedTrampoline() {
-        onIntentStarted();
-
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+        onIntentStarted(mTrampolineActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt());
 
         // A second, distinct, activity launch is coalesced into the current app launch sequence.
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
+        mLaunchTopByTrampoline = true;
+        onIntentStarted(mTopActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
 
+        // The observer shouldn't receive onActivityLaunched for an existing transition.
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
-    private void notifyTransitionStarting() {
-        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-                SystemClock.elapsedRealtimeNanos());
+    private void notifyActivityLaunching(Intent intent) {
+        final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+        mLaunchingState = mActivityMetricsLogger.notifyActivityLaunching(intent,
+                mLaunchTopByTrampoline ? mTrampolineActivity : null /* caller */);
+        if (mLaunchTopByTrampoline) {
+            // The transition of TrampolineActivity has not been completed, so when the next
+            // activity is starting from it, the same launching state should be returned.
+            assertWithMessage("Use existing launching state for a caller in active transition")
+                    .that(previousState).isEqualTo(mLaunchingState);
+        }
+    }
+
+    private void notifyActivityLaunched(int resultCode, ActivityRecord activity) {
+        mActivityMetricsLogger.notifyActivityLaunched(mLaunchingState, resultCode, activity);
+    }
+
+    private void notifyTransitionStarting(ActivityRecord activity) {
+        final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+        reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
+        mActivityMetricsLogger.notifyTransitionStarting(reasons);
     }
 
     private void notifyWindowsDrawn(ActivityRecord r) {
-        mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(),
-                SystemClock.elapsedRealtimeNanos());
+        mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos());
     }
 
     @Test
     public void testOnActivityLaunchFinishedTrampoline() {
         onActivityLaunchedTrampoline();
 
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         notifyWindowsDrawn(mTrampolineActivity);
 
+        assertWithMessage("Trampoline activity is drawn but the top activity is not yet")
+                .that(mLaunchingState.allDrawn()).isFalse();
+
         notifyWindowsDrawn(mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -244,13 +272,23 @@
     }
 
     @Test
+    public void testDoNotCountInvisibleActivityToBeDrawn() {
+        onActivityLaunchedTrampoline();
+        mTrampolineActivity.setVisibility(false);
+        notifyWindowsDrawn(mTopActivity);
+
+        assertWithMessage("Trampoline activity is invisble so there should be no undrawn windows")
+                .that(mLaunchingState.allDrawn()).isTrue();
+    }
+
+    @Test
     public void testOnActivityLaunchCancelledTrampoline() {
         onActivityLaunchedTrampoline();
 
         mTopActivity.mDrawn = true;
 
         // Cannot time already-visible activities.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+        notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
         verifyNoMoreInteractions(mLaunchObserver);
@@ -268,4 +306,32 @@
                 .that(activityRecordToProto(mTrampolineActivity).length)
                 .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
     }
+
+    @Test
+    public void testConcurrentLaunches() {
+        onActivityLaunched(mTopActivity);
+        final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+
+        final ActivityRecord otherActivity = new ActivityBuilder(mService)
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "OtherActivity"))
+                .setCreateTask(true)
+                .build();
+        // Assume the calling uid is different from the uid of TopActivity, so a new launching
+        // state should be created here.
+        onActivityLaunched(otherActivity);
+
+        assertWithMessage("Different callers should get 2 indepedent launching states")
+                .that(previousState).isNotEqualTo(mLaunchingState);
+
+        notifyTransitionStarting(otherActivity);
+        notifyWindowsDrawn(otherActivity);
+
+        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(otherActivity), anyLong());
+
+        // The first transition should still be valid.
+        notifyTransitionStarting(mTopActivity);
+        notifyWindowsDrawn(mTopActivity);
+
+        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+    }
 }
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 b1b386b..c2b8827 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -914,7 +914,7 @@
         // Add another stack to become focused and make the activity there visible. This way it
         // simulates finishing in non-focused stack in split-screen.
         final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
-        final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
+        final ActivityRecord focusedActivity = stack.getTopMostActivity();
         focusedActivity.nowVisible = true;
         focusedActivity.mVisibleRequested = true;
         focusedActivity.setState(RESUMED, "test");
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 ba54859..c8795ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -241,7 +241,7 @@
 
         final RootActivityContainer.FindTaskResult result =
                 new RootActivityContainer.FindTaskResult();
-        mStack.findTaskLocked(r, result);
+        result.process(r, mStack);
 
         assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
         assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
@@ -266,14 +266,14 @@
         final ActivityRecord r1 = new ActivityBuilder(mService).setComponent(
                 target).setTargetActivity(targetActivity).build();
         RootActivityContainer.FindTaskResult result = new RootActivityContainer.FindTaskResult();
-        mStack.findTaskLocked(r1, result);
+        result.process(r1, mStack);
         assertThat(result.mRecord).isNotNull();
 
         // Using alias activity to find task.
         final ActivityRecord r2 = new ActivityBuilder(mService).setComponent(
                 alias).setTargetActivity(targetActivity).build();
         result = new RootActivityContainer.FindTaskResult();
-        mStack.findTaskLocked(r2, result);
+        result.process(r2, mStack);
         assertThat(result.mRecord).isNotNull();
     }
 
@@ -846,9 +846,9 @@
         mStack.mResumedActivity = secondActivity;
 
         // Note the activities have non-null ActivityRecord.app, so it won't remove directly.
-        mStack.finishDisabledPackageActivitiesLocked(firstActivity.packageName,
-                null /* filterByClasses */, true /* doit */, true /* evenPersistent */,
-                UserHandle.USER_ALL);
+        mRootActivityContainer.mFinishDisabledPackageActivitiesHelper.process(
+                firstActivity.packageName, null /* filterByClasses */, true /* doit */,
+                true /* evenPersistent */, UserHandle.USER_ALL);
 
         // If the activity is disabled with {@link android.content.pm.PackageManager#DONT_KILL_APP}
         // the activity should still follow the normal flow to finish and destroy.
@@ -875,9 +875,9 @@
 
         assertEquals(2, mTask.getChildCount());
 
-        mStack.finishDisabledPackageActivitiesLocked(activity.packageName,
-                null  /* filterByClasses */, true /* doit */, true /* evenPersistent */,
-                UserHandle.USER_ALL);
+        mRootActivityContainer.mFinishDisabledPackageActivitiesHelper.process(
+                activity.packageName, null  /* filterByClasses */, true /* doit */,
+                true /* evenPersistent */, UserHandle.USER_ALL);
 
         // 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
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 4165052..d78e3af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -167,16 +167,8 @@
 
     @Test
     public void testSuspendedPackage() {
-        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
         final String suspendingPackage = "com.test.suspending.package";
-        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
-                .setMessage("Test Message")
-                .setIcon(0x11110001)
-                .build();
-        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(suspendingPackage);
-        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
-                TEST_USER_ID)).thenReturn(dialogInfo);
+        final SuspendDialogInfo dialogInfo = suspendPackage(suspendingPackage);
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
 
@@ -190,6 +182,19 @@
         assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000));
     }
 
+    private SuspendDialogInfo suspendPackage(String suspendingPackage) {
+        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setMessage("Test Message")
+                .setIcon(0x11110001)
+                .build();
+        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(suspendingPackage);
+        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
+                TEST_USER_ID)).thenReturn(dialogInfo);
+        return dialogInfo;
+    }
+
     @Test
     public void testInterceptQuietProfile() {
         // GIVEN that the user the activity is starting as is currently in quiet mode
@@ -204,6 +209,20 @@
     }
 
     @Test
+    public void testInterceptQuietProfileWhenPackageSuspended() {
+        suspendPackage("com.test.suspending.package");
+        // GIVEN that the user the activity is starting as is currently in quiet mode
+        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the quiet mode intent
+        assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
+                .filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
     public void testWorkChallenge() {
         // GIVEN that the user the activity is starting as is currently locked
         when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
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 9f3bd75..0382bf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -54,6 +54,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
 import android.view.Surface;
 import android.view.WindowManager;
 
@@ -334,7 +335,7 @@
         mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         mDisplayContent.getDisplayRotation().setFixedToUserRotation(
-                DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
+                IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
         reset(mTask);
         mActivity.reportDescendantOrientationChangeIfNeeded();
         verify(mTask).onConfigurationChanged(any(Configuration.class));
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 5070fb6..3eda980 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -82,6 +82,7 @@
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IDisplayWindowRotationController;
 import android.view.ISystemGestureExclusionListener;
+import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.ViewRootImpl;
@@ -704,7 +705,7 @@
     public void testAllowsTopmostFullscreenOrientation() {
         final DisplayContent dc = createNewDisplay();
         dc.getDisplayRotation().setFixedToUserRotation(
-                DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
+                IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
 
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
@@ -735,7 +736,7 @@
     public void testOnDescendantOrientationRequestChanged() {
         final DisplayContent dc = createNewDisplay();
         dc.getDisplayRotation().setFixedToUserRotation(
-                DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
+                IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
@@ -757,7 +758,7 @@
     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
         final DisplayContent dc = createNewDisplay();
         dc.getDisplayRotation().setFixedToUserRotation(
-                DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
+                IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index bb4d35f..da807d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -19,6 +19,9 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -32,9 +35,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e375f83..4d2183b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -18,6 +18,9 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
 
@@ -28,9 +31,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index e2bdf87..4257249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -73,6 +73,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Tests for the {@link RootActivityContainer} class.
@@ -149,9 +150,7 @@
         final Task task = stack.getAllTasks().get(0);
         final ArrayList<ActivityRecord> stackActivities = new ArrayList<>();
 
-        for (int i = 0; i < task.getChildCount(); i++) {
-            stackActivities.add(task.getChildAt(i));
-        }
+        task.forAllActivities((Consumer<ActivityRecord>) stackActivities::add, false);
 
         assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + stackActivities,
                 stackActivities.size(), activities != null ? activities.length : 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index c8f81f4..599edb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -27,6 +27,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
 
@@ -36,7 +37,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
-import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -471,8 +471,9 @@
         // Add an extra activity on top of the root one
         new ActivityBuilder(mService).setTask(task).build();
 
-        assertEquals("The root activity in the task must be reported.",
-                0, task.findRootIndex(false /* effectiveRoot*/));
+        assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
+                task.getRootActivity(
+                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /**
@@ -483,14 +484,15 @@
     public void testFindRootIndex_finishing() {
         final Task task = getTestTask();
         // Add extra two activities and mark the two on the bottom as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
         activity1.finishing = true;
         new ActivityBuilder(mService).setTask(task).build();
 
         assertEquals("The first non-finishing activity in the task must be reported.",
-                2, task.findRootIndex(false /* effectiveRoot*/));
+                task.getChildAt(2), task.getRootActivity(
+                        true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /**
@@ -504,7 +506,8 @@
         new ActivityBuilder(mService).setTask(task).build();
 
         assertEquals("The root activity in the task must be reported.",
-                0, task.findRootIndex(true /* effectiveRoot*/));
+                task.getChildAt(0), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /**
@@ -516,14 +519,15 @@
         final Task task = getTestTask();
         // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
         // one above as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
         activity1.finishing = true;
         new ActivityBuilder(mService).setTask(task).build();
 
         assertEquals("The first non-finishing activity and non-relinquishing task identity "
-                        + "must be reported.", 2, task.findRootIndex(true /* effectiveRoot*/));
+                + "must be reported.", task.getChildAt(2), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /**
@@ -534,10 +538,11 @@
     public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
         final Task task = getTestTask();
         // Set relinquishTaskIdentity for the only activity in the task
-        task.getChildAt(0).info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+        task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
 
         assertEquals("The root activity in the task must be reported.",
-                0, task.findRootIndex(true /* effectiveRoot*/));
+                task.getChildAt(0), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /**
@@ -548,13 +553,14 @@
     public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
         final Task task = getTestTask();
         // Set relinquishTaskIdentity for all activities in the task
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
         activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
 
         assertEquals("The topmost activity in the task must be reported.",
-                task.getChildCount() - 1, task.findRootIndex(true /* effectiveRoot*/));
+                task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -565,7 +571,7 @@
         new ActivityBuilder(mService).setTask(task).build();
 
         assertEquals("The root activity in the task must be reported.",
-                task.getChildAt(0), task.getRootActivity());
+                task.getBottomMostActivity(), task.getRootActivity());
     }
 
     /**
@@ -577,7 +583,7 @@
         // Add an extra activity on top of the root one
         new ActivityBuilder(mService).setTask(task).build();
         // Mark the root as finishing
-        task.getChildAt(0).finishing = true;
+        task.getBottomMostActivity().finishing = true;
 
         assertEquals("The first non-finishing activity in the task must be reported.",
                 task.getChildAt(1), task.getRootActivity());
@@ -590,13 +596,13 @@
     public void testGetRootActivity_relinquishTaskIdentity() {
         final Task task = getTestTask();
         // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         // Add an extra activity on top of the root one.
         new ActivityBuilder(mService).setTask(task).build();
 
         assertEquals("The root activity in the task must be reported.",
-                task.getChildAt(0), task.getRootActivity());
+                task.getBottomMostActivity(), task.getRootActivity());
     }
 
     /**
@@ -607,7 +613,7 @@
     public void testGetRootActivity_allFinishing() {
         final Task task = getTestTask();
         // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
         // Add an extra activity on top of the root one and mark it as finishing
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -623,7 +629,7 @@
     public void testIsRootActivity() {
         final Task task = getTestTask();
         // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
         // Add an extra activity on top of the root one.
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -640,7 +646,7 @@
     public void testIsRootActivity_allFinishing() {
         final Task task = getTestTask();
         // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
         // Add an extra activity on top of the root one and mark it as finishing
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -657,10 +663,10 @@
     @Test
     public void testGetTaskForActivity() {
         final Task task0 = getTestTask();
-        final ActivityRecord activity0 = task0.getChildAt(0);
+        final ActivityRecord activity0 = task0.getBottomMostActivity();
 
         final Task task1 = getTestTask();
-        final ActivityRecord activity1 = task1.getChildAt(0);
+        final ActivityRecord activity1 = task1.getBottomMostActivity();
 
         assertEquals(task0.mTaskId,
                 ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
@@ -676,7 +682,7 @@
     public void testGetTaskForActivity_onlyRoot_finishing() {
         final Task task = getTestTask();
         // Make the current root activity finishing
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
         // Add an extra activity on top - this will be the new root
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -699,7 +705,7 @@
     public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
         final Task task = getTestTask();
         // Make the current root activity relinquish task identity
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         // Add an extra activity on top - this will be the new root
         final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -722,7 +728,7 @@
     public void testGetTaskForActivity_notOnlyRoot() {
         final Task task = getTestTask();
         // Mark the bottom-most activity as finishing.
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.finishing = true;
 
         // Add an extra activity on top of the root one and make it relinquish task identity
@@ -747,7 +753,7 @@
     public void testUpdateEffectiveIntent() {
         // Test simple case with a single activity.
         final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
 
         spyOn(task);
         task.updateEffectiveIntent();
@@ -762,7 +768,7 @@
     public void testUpdateEffectiveIntent_rootFinishing() {
         // Test simple case with a single activity.
         final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         // Mark the bottom-most activity as finishing.
         activity0.finishing = true;
         // Add an extra activity on top of the root one
@@ -782,7 +788,7 @@
     public void testUpdateEffectiveIntent_allFinishing() {
         // Test simple case with a single activity.
         final Task task = getTestTask();
-        final ActivityRecord activity0 = task.getChildAt(0);
+        final ActivityRecord activity0 = task.getBottomMostActivity();
         // Mark the bottom-most activity as finishing.
         activity0.finishing = true;
         // Add an extra activity on top of the root one and make it relinquish task identity
@@ -818,7 +824,7 @@
                 task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
     }
 
-    private byte[] serializeToBytes(Task r) throws IOException, XmlPullParserException {
+    private byte[] serializeToBytes(Task r) throws Exception {
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             final XmlSerializer serializer = Xml.newSerializer();
             serializer.setOutput(os, "UTF-8");
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b428530..221f8f1 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,6 +16,10 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -32,6 +36,7 @@
      * Builder class for {@link ConnectionRequest}
      * @hide
      */
+    @TestApi // For convenience in CTS tests
     public static final class Builder {
         private PhoneAccountHandle mAccountHandle;
         private Uri mAddress;
@@ -48,7 +53,7 @@
          * Sets the phone account handle for the resulting {@link ConnectionRequest}
          * @param accountHandle The accountHandle which should be used to place the call.
          */
-        public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
+        public @NonNull Builder setAccountHandle(@NonNull PhoneAccountHandle accountHandle) {
             this.mAccountHandle = accountHandle;
             return this;
         }
@@ -58,7 +63,7 @@
          * @param address The address(e.g., phone number) to which the {@link Connection} is to
          *                connect.
          */
-        public Builder setAddress(Uri address) {
+        public @NonNull Builder setAddress(@NonNull Uri address) {
             this.mAddress = address;
             return this;
         }
@@ -67,7 +72,7 @@
          * Sets the extras bundle for the resulting {@link ConnectionRequest}
          * @param extras Application-specific extra data.
          */
-        public Builder setExtras(Bundle extras) {
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
             this.mExtras = extras;
             return this;
         }
@@ -76,7 +81,7 @@
          * Sets the video state for the resulting {@link ConnectionRequest}
          * @param videoState Determines the video state for the connection.
          */
-        public Builder setVideoState(int videoState) {
+        public @NonNull Builder setVideoState(int videoState) {
             this.mVideoState = videoState;
             return this;
         }
@@ -85,7 +90,7 @@
          * Sets the Telecom call ID for the resulting {@link ConnectionRequest}
          * @param telecomCallId The telecom call ID.
          */
-        public Builder setTelecomCallId(String telecomCallId) {
+        public @NonNull Builder setTelecomCallId(@NonNull String telecomCallId) {
             this.mTelecomCallId = telecomCallId;
             return this;
         }
@@ -97,7 +102,7 @@
          *                                 its own incoming call UI for an incoming call.  When
          *                                 {@code false}, Telecom shows the incoming call UI.
          */
-        public Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) {
+        public @NonNull Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) {
             this.mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
             return this;
         }
@@ -107,7 +112,8 @@
          * resulting {@link ConnectionRequest}
          * @param rttPipeFromInCall The data pipe to read from.
          */
-        public Builder setRttPipeFromInCall(ParcelFileDescriptor rttPipeFromInCall) {
+        public @NonNull Builder setRttPipeFromInCall(
+                @NonNull ParcelFileDescriptor rttPipeFromInCall) {
             this.mRttPipeFromInCall = rttPipeFromInCall;
             return this;
         }
@@ -117,12 +123,16 @@
          * resulting {@link ConnectionRequest}
          * @param rttPipeToInCall The data pipe to write to.
          */
-        public Builder setRttPipeToInCall(ParcelFileDescriptor rttPipeToInCall) {
+        public @NonNull Builder setRttPipeToInCall(@NonNull ParcelFileDescriptor rttPipeToInCall) {
             this.mRttPipeToInCall = rttPipeToInCall;
             return this;
         }
 
-        public ConnectionRequest build() {
+        /**
+         * Build the {@link ConnectionRequest}
+         * @return Result of the builder
+         */
+        public @NonNull ConnectionRequest build() {
             return new ConnectionRequest(
                     mAccountHandle,
                     mAddress,
@@ -261,7 +271,9 @@
      * @return The Telecom ID.
      * @hide
      */
-    public String getTelecomCallId() {
+    @SystemApi
+    @TestApi
+    public @Nullable String getTelecomCallId() {
         return mTelecomCallId;
     }
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4016943..39d741c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1029,12 +1029,16 @@
      * by the user.
      *
      * @param includeDisabledAccounts When {@code true}, disabled phone accounts will be included,
-     *                                when {@code false}, only
+     *                                when {@code false}, only enabled phone accounts will be
+     *                                included.
      * @return A list of {@code PhoneAccountHandle} objects.
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
-    public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
+    @SystemApi
+    @TestApi
+    @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
+    public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccounts(
+            boolean includeDisabledAccounts) {
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getCallCapablePhoneAccounts(
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index a0aa60b..0320f75 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -80,6 +80,7 @@
         public static final int EUTRAN = 3;
         public static final int CDMA2000 = 4;
         public static final int IWLAN = 5;
+        public static final int NGRAN = 6;
 
         /** @hide */
         private AccessNetworkType() {}
@@ -93,6 +94,7 @@
                 case EUTRAN: return "EUTRAN";
                 case CDMA2000: return "CDMA2000";
                 case IWLAN: return "IWLAN";
+                case NGRAN: return "NGRAN";
                 default: return Integer.toString(type);
             }
         }
@@ -247,6 +249,54 @@
         private CdmaBands() {};
     }
 
+    /**
+     * Frequency bands for NGRAN
+     */
+    public static final class NgranBands {
+        /** FR1 bands */
+        public static final int BAND_1 = 1;
+        public static final int BAND_2 = 2;
+        public static final int BAND_3 = 3;
+        public static final int BAND_5 = 5;
+        public static final int BAND_7 = 7;
+        public static final int BAND_8 = 8;
+        public static final int BAND_12 = 12;
+        public static final int BAND_20 = 20;
+        public static final int BAND_25 = 25;
+        public static final int BAND_28 = 28;
+        public static final int BAND_34 = 34;
+        public static final int BAND_38 = 38;
+        public static final int BAND_39 = 39;
+        public static final int BAND_40 = 40;
+        public static final int BAND_41 = 41;
+        public static final int BAND_50 = 50;
+        public static final int BAND_51 = 51;
+        public static final int BAND_66 = 66;
+        public static final int BAND_70 = 70;
+        public static final int BAND_71 = 71;
+        public static final int BAND_74 = 74;
+        public static final int BAND_75 = 75;
+        public static final int BAND_76 = 76;
+        public static final int BAND_77 = 77;
+        public static final int BAND_78 = 78;
+        public static final int BAND_79 = 79;
+        public static final int BAND_80 = 80;
+        public static final int BAND_81 = 81;
+        public static final int BAND_82 = 82;
+        public static final int BAND_83 = 83;
+        public static final int BAND_84 = 84;
+        public static final int BAND_86 = 86;
+
+        /** FR2 bands */
+        public static final int BAND_257 = 257;
+        public static final int BAND_258 = 258;
+        public static final int BAND_260 = 260;
+        public static final int BAND_261 = 261;
+
+        /** @hide */
+        private NgranBands() {};
+    }
+
     /** @hide */
     private AccessNetworkConstants() {};
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 479b144..98d7661 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1042,6 +1042,9 @@
      *
      * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
      * used to set the IMS stack's RTT enabled state.
+     *
+     * @deprecated -- this flag no longer does anything. Remove once the new behavior is verified.
+     *
      * @hide
      */
     public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
@@ -3523,7 +3526,7 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
-        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index e65b048ec..cb8fdf0 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -46,11 +46,6 @@
     protected static final int NUM_SIGNAL_STRENGTH_THRESHOLDS = NUM_SIGNAL_STRENGTH_BINS - 1;
 
     /** @hide */
-    public static final String[] SIGNAL_STRENGTH_NAMES = {
-        "none", "poor", "moderate", "good", "great"
-    };
-
-    /** @hide */
     protected CellSignalStrength() {
     }
 
@@ -157,4 +152,12 @@
         if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
         return value;
     }
+
+    /**
+     * Returns the number of signal strength levels.
+     * @return Number of signal strength levels, enforced to be 5
+     */
+    public static final int getNumSignalStrengthLevels() {
+        return NUM_SIGNAL_STRENGTH_BINS;
+    }
 }
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 690e44a..a403095 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -97,8 +97,9 @@
      * Returns the frequency bands that need to be scanned.
      *
      * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
-     * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}, and
-     * it depends on the returned value of {@link #getRadioAccessNetwork()}.
+     * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand},
+     * and {@link AccessNetworkConstants.NgranBands}, and it depends on
+     * the returned value of {@link #getRadioAccessNetwork()}.
      */
     public int[] getBands() {
         return mBands == null ? null : mBands.clone();
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 480c9d9..9aafc1b 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -60,12 +60,6 @@
     @UnsupportedAppUsage
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
 
-    /** SIGNAL_STRENGTH_NAMES is currently used by BatteryStats, but to-be-removed soon. */
-    /** @hide */
-    public static final String[] SIGNAL_STRENGTH_NAMES = {
-        "none", "poor", "moderate", "good", "great"
-    };
-
     /**
      * Indicates the invalid measures of signal strength.
      *
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index c7f9529..045d1eb 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -533,7 +533,8 @@
                 + ", priority=" + mPriority
                 + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
                 + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
-                + ", maximumWaitingTime = " + mMaximumWaitTimeSec
+                + ", maximumWaitingTime=" + mMaximumWaitTimeSec
+                + ", received time=" + mReceivedTimeMillis
                 + ", slotIndex = " + mSlotIndex
                 + ", geo=" + (mGeometries != null
                 ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1dd1185..fc26122 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -86,7 +86,6 @@
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.IOns;
@@ -5176,13 +5175,11 @@
      * @return the current call state.
      */
     public @CallState int getCallState() {
-        try {
-            ITelecomService telecom = getTelecomService();
-            if (telecom != null) {
-                return telecom.getCallState();
+        if (mContext != null) {
+            TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+            if (telecomManager != null) {
+                return telecomManager.getCallState();
             }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecomService#getCallState", e);
         }
         return CALL_STATE_IDLE;
     }
@@ -5341,13 +5338,6 @@
         return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
     }
 
-    /**
-    * @hide
-    */
-    private ITelecomService getTelecomService() {
-        return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
-    }
-
     private ITelephonyRegistry getTelephonyRegistry() {
         return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
     }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 663b09a..f4b2cef 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -18,9 +18,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,12 +29,13 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * This class enables an application to get details on why a method call failed.
- *
- * @hide
+ * Provides details on why an IMS call failed. Applications can use the methods in this class to
+ * get local or network fault behind an IMS services failure. For example, if the code is
+ * CODE_CALL_BARRED, then the call was blocked by network call barring configuration and it is not
+ * the device's bug and the user can retry the call when network lift the barring.
+ * Typical use case includes call backs when IMS call state changed with this class as a param
+ * containing details on why IMS call changed state/failed.
  */
-@SystemApi
-@TestApi
 public final class ImsReasonInfo implements Parcelable {
 
     /**
@@ -1096,9 +1098,11 @@
     /**
      * Network string error messages.
      * mExtraMessage may have these values.
+     * @hide
      */
-    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED
-            = "Forbidden. Not Authorized for Service";
+    @SystemApi
+    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED =
+            "Forbidden. Not Authorized for Service";
 
 
     /*
@@ -1106,21 +1110,21 @@
      * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
      */
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS
      */
     public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS and do not notify the user.
      */
     public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS by using the settings.
@@ -1130,15 +1134,18 @@
 
     // For main reason code
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getCode()}")
     public int mCode;
     // For the extra code value; it depends on the code value.
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraCode()}")
     public int mExtraCode;
     // For the additional message of the reason info.
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraMessage()}")
     public String mExtraMessage;
 
     /** @hide */
@@ -1162,7 +1169,7 @@
         mExtraMessage = null;
     }
 
-    public ImsReasonInfo(int code, int extraCode, String extraMessage) {
+    public ImsReasonInfo(@ImsCode int code, int extraCode, @Nullable String extraMessage) {
         mCode = code;
         mExtraCode = extraCode;
         mExtraMessage = extraMessage;
@@ -1186,7 +1193,7 @@
      * @return an optional OEM specified string that provides extra information about the operation
      * result.
      */
-    public String getExtraMessage() {
+    public @Nullable String getExtraMessage() {
         return mExtraMessage;
     }
 
@@ -1205,13 +1212,13 @@
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeInt(mCode);
         out.writeInt(mExtraCode);
         out.writeString(mExtraMessage);
     }
 
-    public static final @android.annotation.NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
+    public static final @NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
         @Override
         public ImsReasonInfo createFromParcel(Parcel in) {
             return new ImsReasonInfo(in);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 6e20621..96ddf22 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -390,7 +390,6 @@
     private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
             int uid, String callingPackage, String message) {
         boolean isPreinstalled = false;
-        boolean isPrivApp = false;
         ApplicationInfo callingPackageInfo = null;
         try {
             callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
@@ -398,9 +397,6 @@
             if (callingPackageInfo != null) {
                 if (callingPackageInfo.isSystemApp()) {
                     isPreinstalled = true;
-                    if (callingPackageInfo.isPrivilegedApp()) {
-                        isPrivApp = true;
-                    }
                 }
             }
         } catch (PackageManager.NameNotFoundException e) {
@@ -423,10 +419,10 @@
             }
             invokedMethods.add(message);
             StatsLog.write(StatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message,
-                    isPreinstalled, isPrivApp);
+                    isPreinstalled, false);
         }
         Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
-                + ":isPreinstalled=" + isPreinstalled + ":isPrivApp=" + isPrivApp);
+                + ":isPreinstalled=" + isPreinstalled);
         // if the target SDK is pre-Q then check if the calling package would have previously
         // had access to device identifiers.
         if (callingPackageInfo != null && (
diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
new file mode 100644
index 0000000..a28d65c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.telephony.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+
+import java.io.PrintWriter;
+
+/**
+ * This class provides various util functions
+ */
+public final class TelephonyUtils {
+    public static boolean IS_USER = "user".equals(android.os.Build.TYPE);
+    public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
+
+    /**
+     * Verify that caller holds {@link android.Manifest.permission#DUMP}.
+     *
+     * @return true if access should be granted.
+     */
+    public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
+        if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump " + tag + " from from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " due to missing android.permission.DUMP permission");
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /** Returns an empty string if the input is {@code null}. */
+    public static String emptyIfNull(@Nullable String str) {
+        return str == null ? "" : str;
+    }
+
+    /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
+    public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
+        throw new RuntimeException(remoteException);
+    }
+
+    /**
+     * Returns a {@link ComponentInfo} from the {@link ResolveInfo},
+     * or throws an {@link IllegalStateException} if not available.
+     */
+    public static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
+        if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
+        if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
+        if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
+        throw new IllegalStateException("Missing ComponentInfo!");
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 0787d82..51bae3a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -29,9 +29,13 @@
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
+import android.graphics.RuntimeShader;
 import android.os.Bundle;
 import android.view.View;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
 @SuppressWarnings({"UnusedDeclaration"})
 public class ColorFiltersMutateActivity extends Activity {
     @Override
@@ -47,12 +51,21 @@
         private final Paint mColorMatrixPaint;
         private final Paint mLightingPaint;
         private final Paint mBlendPaint;
+        private final Paint mShaderPaint;
 
         private float mSaturation = 0.0f;
         private int mLightAdd = 0;
         private int mLightMul = 0;
         private int mPorterDuffColor = 0;
 
+        static final String sSkSL =
+                "uniform float param1;\n"
+                + "void main(float x, float y, inout half4 color) {\n"
+                + "color = half4(color.r, half(param1), color.b, 1.0);\n"
+                + "}\n";
+
+        private byte[] mUniforms = new byte[4];
+
         BitmapsView(Context c) {
             super(c);
 
@@ -70,6 +83,10 @@
             mBlendPaint = new Paint();
             mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
 
+            mShaderPaint = new Paint();
+            mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, true));
+            setShaderParam1(0.0f);
+
             ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f);
             sat.setDuration(1000);
             sat.setRepeatCount(ObjectAnimator.INFINITE);
@@ -96,6 +113,12 @@
             color.setRepeatCount(ObjectAnimator.INFINITE);
             color.setRepeatMode(ObjectAnimator.REVERSE);
             color.start();
+
+            ObjectAnimator shaderUniform = ObjectAnimator.ofFloat(this, "shaderParam1", 1.0f);
+            shaderUniform.setDuration(1000);
+            shaderUniform.setRepeatCount(ObjectAnimator.INFINITE);
+            shaderUniform.setRepeatMode(ObjectAnimator.REVERSE);
+            shaderUniform.start();
         }
 
         public int getPorterDuffColor() {
@@ -148,6 +171,23 @@
             return mSaturation;
         }
 
+        public void setShaderParam1(float value) {
+            RuntimeShader shader = (RuntimeShader) mShaderPaint.getShader();
+            ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+            buffer.putFloat(value);
+            shader.updateUniforms(mUniforms);
+            invalidate();
+        }
+
+        // If either valueFrom or valueTo is null, then a getter function will also be derived
+        // and called by the animator class.
+        public float getShaderParam1() {
+            ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+            return buffer.getFloat();
+        }
+
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
@@ -163,6 +203,10 @@
 
             canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
             canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBlendPaint);
+
+            canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+            canvas.drawRect(0.0f, 0.0f, mBitmap1.getWidth(), mBitmap1.getHeight(),
+                    mShaderPaint);
             canvas.restore();
 
             canvas.save();
@@ -174,6 +218,10 @@
 
             canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
             canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBlendPaint);
+
+            canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+            canvas.drawRoundRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), 20, 20,
+                    mShaderPaint);
             canvas.restore();
         }
     }
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index dcacb6d..638b6d1 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -32,7 +32,7 @@
     <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
@@ -45,6 +45,8 @@
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.NETWORK_STACK" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
+    <uses-permission android:name="android.permission.NETWORK_FACTORY" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c4e353b..fd3ed7d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5644,6 +5644,7 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
+    @Ignore // 40%+ flakiness : figure out why and re-enable.
     @Test
     public final void testBatteryStatsNetworkType() throws Exception {
         final LinkProperties cellLp = new LinkProperties();
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 2738daa..39f849c 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -265,6 +265,8 @@
         assertFalse(mPermissionMonitor.hasNetworkPermission(app));
         app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS);
         assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+        app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL);
+        assertFalse(mPermissionMonitor.hasNetworkPermission(app));
     }
 
     @Test
@@ -274,7 +276,7 @@
                 PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
-        assertTrue(hasRestrictedNetworkPermission(
+        assertFalse(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
@@ -283,7 +285,7 @@
 
         assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+                PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
     }
 
     @Test
@@ -291,14 +293,14 @@
         doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
         assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE));
+                PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
         doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
         assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE));
+                PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
     }
@@ -319,7 +321,7 @@
 
         assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+                PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
         assertFalse(hasRestrictedNetworkPermission(
                 PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE));
     }
@@ -337,7 +339,7 @@
     public void testHasUseBackgroundNetworksPermission() throws Exception {
         assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
         assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID);
-        assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_WIFI_STATE);
+        assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL);
         assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
         assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
 
@@ -348,8 +350,9 @@
 
         assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
         assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
-        assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2,
+        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
                 CONNECTIVITY_INTERNAL);
+        assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
     }
 
     private class NetdMonitor {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 99a686b..4555caa 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1297,7 +1297,8 @@
       return false;
     }
 
-    proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
+    proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules,
+                           options_.no_proguard_location_reference);
     fout.Flush();
 
     if (fout.HadError()) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index aea2432..4722358 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -56,6 +56,7 @@
   bool generate_conditional_proguard_rules = false;
   bool generate_minimal_proguard_rules = false;
   bool generate_non_final_ids = false;
+  bool no_proguard_location_reference = false;
   std::vector<std::string> javadoc_annotations;
   Maybe<std::string> private_symbols;
 
@@ -215,6 +216,9 @@
         "Generates R.java without the final modifier. This is implied when\n"
             "--static-lib is specified.",
         &options_.generate_non_final_ids);
+    AddOptionalSwitch("--no-proguard-location-reference",
+        "Keep proguard rules files from having a reference to the source file",
+        &options_.no_proguard_location_reference);
     AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
         &stable_id_file_path_);
     AddOptionalFlag("--emit-ids",
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 806f4e3..b06607e 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -393,11 +393,15 @@
   return true;
 }
 
-void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep,
+                  bool no_location_reference) {
+
   Printer printer(out);
   for (const auto& entry : keep_set.manifest_class_set_) {
-    for (const UsageLocation& location : entry.second) {
-      printer.Print("# Referenced at ").Println(location.source.to_string());
+    if (!no_location_reference) {
+      for (const UsageLocation& location : entry.second) {
+        printer.Print("# Referenced at ").Println(location.source.to_string());
+      }
     }
     printer.Print("-keep class ").Print(entry.first).Println(" { <init>(); }");
   }
@@ -414,7 +418,9 @@
 
     if (can_be_conditional) {
       for (const UsageLocation& location : locations) {
-        printer.Print("# Referenced at ").Println(location.source.to_string());
+        if (!no_location_reference) {
+          printer.Print("# Referenced at ").Println(location.source.to_string());
+        }
         printer.Print("-if class **.R$layout { int ")
             .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
             .Println("; }");
@@ -424,8 +430,10 @@
         printer.Println("); }");
       }
     } else {
-      for (const UsageLocation& location : entry.second) {
-        printer.Print("# Referenced at ").Println(location.source.to_string());
+      if (!no_location_reference) {
+        for (const UsageLocation& location : entry.second) {
+          printer.Print("# Referenced at ").Println(location.source.to_string());
+        }
       }
 
       printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
@@ -436,8 +444,10 @@
   }
 
   for (const auto& entry : keep_set.method_set_) {
-    for (const UsageLocation& location : entry.second) {
-      printer.Print("# Referenced at ").Println(location.source.to_string());
+    if (!no_location_reference) {
+      for (const UsageLocation& location : entry.second) {
+        printer.Print("# Referenced at ").Println(location.source.to_string());
+      }
     }
     printer.Print("-keepclassmembers class * { *** ").Print(entry.first.name)
         .Print("(").Print(entry.first.signature).Println("); }");
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index b15df59..a01b64d 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -70,7 +70,8 @@
   }
 
  private:
-  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
+  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+                           bool no_location_reference);
 
   friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                                std::set<UsageLocation>* locations);
@@ -89,7 +90,8 @@
 
 bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
 
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+                  bool no_location_reference);
 
 bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                       std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 25b55ab..8720597 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -30,7 +30,7 @@
 std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
   std::string out;
   StringOutputStream sout(&out);
-  proguard::WriteKeepSet(set, &sout, minimal_rules);
+  proguard::WriteKeepSet(set, &sout, minimal_rules, false);
   sout.Flush();
   return out;
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 4a89c66..032f66a 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -142,6 +142,8 @@
 
     boolean startSoftAp(in WifiConfiguration wifiConfig);
 
+    boolean startTetheredHotspot(in SoftApConfiguration softApConfig);
+
     boolean stopSoftAp();
 
     int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName,
@@ -159,8 +161,14 @@
     @UnsupportedAppUsage
     WifiConfiguration getWifiApConfiguration();
 
+    SoftApConfiguration getSoftApConfiguration();
+
     boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
 
+    boolean setSoftApConfiguration(in SoftApConfiguration softApConfig, String packageName);
+
+    void notifyUserOfApBandConversion(String packageName);
+
     void enableTdls(String remoteIPAddress, boolean enable);
 
     void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
@@ -184,6 +192,10 @@
 
     void restoreBackupData(in byte[] data);
 
+    byte[] retrieveSoftApBackupData();
+
+    void restoreSoftApBackupData(in byte[] data);
+
     void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData);
 
     void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback);
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 4cc8653..8030bd6 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -16,28 +16,34 @@
 
 package android.net.wifi;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
- * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
+ * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
  *
  * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
- * framework how it should open a hotspot.  It is not meant to describe the network as it will be
- * seen by clients; this role is currently served by {@link WifiConfiguration} (see
- * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}).
+ * framework how it should configure a hotspot.
  *
- * System apps can use this to configure a local-only hotspot using
+ * System apps can use this to configure a tethered hotspot using
+ * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} and
+ * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}
+ * or local-only hotspot using
  * {@link WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
  * WifiManager.LocalOnlyHotspotCallback)}.
  *
@@ -48,25 +54,106 @@
  */
 @SystemApi
 public final class SoftApConfiguration implements Parcelable {
+
+    /**
+     * 2GHz band.
+     * @hide
+     */
+    @SystemApi
+    public static final int BAND_2GHZ = 0;
+
+    /**
+     * 5GHz band.
+     * @hide
+     */
+    @SystemApi
+    public static final int BAND_5GHZ = 1;
+
+    /**
+     * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+     * operating country code and current radio conditions.
+     * @hide
+     */
+    @SystemApi
+    public static final int BAND_ANY = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "BAND_TYPE_" }, value = {
+            BAND_2GHZ,
+            BAND_5GHZ,
+            BAND_ANY,
+    })
+    public @interface BandType {}
+
     /**
      * SSID for the AP, or null for a framework-determined SSID.
      */
     private final @Nullable String mSsid;
+
     /**
      * BSSID for the AP, or null to use a framework-determined BSSID.
      */
     private final @Nullable MacAddress mBssid;
+
     /**
      * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
      */
     private final @Nullable String mWpa2Passphrase;
 
+    /**
+     * This is a network that does not broadcast its SSID, so an
+     * SSID-specific probe request must be used for scans.
+     */
+    private final boolean mHiddenSsid;
+
+    /**
+     * The operating band of the AP.
+     * One of the band types from {@link @BandType}.
+     */
+    private final @BandType int mBand;
+
+    /**
+     * The operating channel of the AP.
+     */
+    private final int mChannel;
+
+    /**
+     * The operating security type of the AP.
+     * One of the security types from {@link @SecurityType}
+     */
+    private final @SecurityType int mSecurityType;
+
+    /**
+     * Security types we support.
+     */
+    /** @hide */
+    @SystemApi
+    public static final int SECURITY_TYPE_OPEN = 0;
+
+    /** @hide */
+    @SystemApi
+    public static final int SECURITY_TYPE_WPA2_PSK = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "SECURITY_TYPE" }, value = {
+        SECURITY_TYPE_OPEN,
+        SECURITY_TYPE_WPA2_PSK,
+    })
+    public @interface SecurityType {}
+
     /** Private constructor for Builder and Parcelable implementation. */
-    private SoftApConfiguration(
-            @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) {
+    private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
+            @Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
+            @SecurityType int securityType) {
         mSsid = ssid;
         mBssid = bssid;
         mWpa2Passphrase = wpa2Passphrase;
+        mHiddenSsid = hiddenSsid;
+        mBand = band;
+        mChannel = channel;
+        mSecurityType = securityType;
     }
 
     @Override
@@ -80,12 +167,31 @@
         SoftApConfiguration other = (SoftApConfiguration) otherObj;
         return Objects.equals(mSsid, other.mSsid)
                 && Objects.equals(mBssid, other.mBssid)
-                && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase);
+                && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase)
+                && mHiddenSsid == other.mHiddenSsid
+                && mBand == other.mBand
+                && mChannel == other.mChannel
+                && mSecurityType == other.mSecurityType;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSsid, mBssid, mWpa2Passphrase);
+        return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
+                mBand, mChannel, mSecurityType);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append("ssid=").append(mSsid);
+        if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString());
+        sbuf.append(" \n Wpa2Passphrase =").append(
+                TextUtils.isEmpty(mWpa2Passphrase) ? "<empty>" : "<non-empty>");
+        sbuf.append(" \n HiddenSsid =").append(mHiddenSsid);
+        sbuf.append(" \n Band =").append(mBand);
+        sbuf.append(" \n Channel =").append(mChannel);
+        sbuf.append(" \n SecurityType=").append(getSecurityType());
+        return sbuf.toString();
     }
 
     @Override
@@ -93,6 +199,10 @@
         dest.writeString(mSsid);
         dest.writeParcelable(mBssid, flags);
         dest.writeString(mWpa2Passphrase);
+        dest.writeBoolean(mHiddenSsid);
+        dest.writeInt(mBand);
+        dest.writeInt(mChannel);
+        dest.writeInt(mSecurityType);
     }
 
     @Override
@@ -107,7 +217,7 @@
             return new SoftApConfiguration(
                     in.readString(),
                     in.readParcelable(MacAddress.class.getClassLoader()),
-                    in.readString());
+                    in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt());
         }
 
         @Override
@@ -116,22 +226,68 @@
         }
     };
 
+    /**
+     * Return String set to be the SSID for the AP.
+     * {@link #setSsid(String)}.
+     */
     @Nullable
     public String getSsid() {
         return mSsid;
     }
 
+    /**
+     * Returns MAC address set to be BSSID for the AP.
+     * {@link #setBssid(MacAddress)}.
+     */
     @Nullable
     public MacAddress getBssid() {
         return mBssid;
     }
 
+    /**
+     * Returns String set to be passphrase for the WPA2-PSK AP.
+     * {@link #setWpa2Passphrase(String)}.
+     */
     @Nullable
     public String getWpa2Passphrase() {
         return mWpa2Passphrase;
     }
 
     /**
+     * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
+     * not (false: broadcasts its SSID) for the AP.
+     * {@link #setHiddenSsid(boolean)}.
+     */
+    public boolean isHiddenSsid() {
+        return mHiddenSsid;
+    }
+
+    /**
+     * Returns {@link BandType} set to be the band for the AP.
+     * {@link #setBand(@BandType int)}.
+     */
+    public @BandType int getBand() {
+        return mBand;
+    }
+
+    /**
+     * Returns Integer set to be the channel for the AP.
+     * {@link #setChannel(int)}.
+     */
+    public int getChannel() {
+        return mChannel;
+    }
+
+    /**
+     * Get security type params which depends on which security passphrase to set.
+     *
+     * @return One of the security types from {@link SecurityType}.
+     */
+    public @SecurityType int getSecurityType() {
+        return mSecurityType;
+    }
+
+    /**
      * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
      * Soft AP.
      *
@@ -142,6 +298,21 @@
         private String mSsid;
         private MacAddress mBssid;
         private String mWpa2Passphrase;
+        private boolean mHiddenSsid;
+        private int mBand;
+        private int mChannel;
+
+        private int setSecurityType() {
+            int securityType = SECURITY_TYPE_OPEN;
+            if (!TextUtils.isEmpty(mWpa2Passphrase)) { // WPA2-PSK network.
+                securityType = SECURITY_TYPE_WPA2_PSK;
+            }
+            return securityType;
+        }
+
+        private void clearAllPassphrase() {
+            mWpa2Passphrase = null;
+        }
 
         /**
          * Constructs a Builder with default values (see {@link Builder}).
@@ -150,6 +321,9 @@
             mSsid = null;
             mBssid = null;
             mWpa2Passphrase = null;
+            mHiddenSsid = false;
+            mBand = BAND_2GHZ;
+            mChannel = 0;
         }
 
         /**
@@ -161,6 +335,9 @@
             mSsid = other.mSsid;
             mBssid = other.mBssid;
             mWpa2Passphrase = other.mWpa2Passphrase;
+            mHiddenSsid = other.mHiddenSsid;
+            mBand = other.mBand;
+            mChannel = other.mChannel;
         }
 
         /**
@@ -170,11 +347,16 @@
          */
         @NonNull
         public SoftApConfiguration build() {
-            return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase);
+            return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
+                mHiddenSsid, mBand, mChannel, setSecurityType());
         }
 
         /**
          * Specifies an SSID for the AP.
+         * <p>
+         * Null SSID only support when configure a local-only hotspot.
+         * <p>
+         * <li>If not set, defaults to null.</li>
          *
          * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
          *             chosen by the framework.
@@ -193,7 +375,10 @@
 
         /**
          * Specifies a BSSID for the AP.
-         *
+         * <p>
+         * Only supported when configuring a local-only hotspot.
+         * <p>
+         * <li>If not set, defaults to null.</li>
          * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
          *              responsible for avoiding collisions.
          * @return Builder for chaining.
@@ -211,8 +396,9 @@
         }
 
         /**
-         * Specifies that this AP should use WPA2-PSK with the given passphrase.  When set to null
-         * and no other encryption method is configured, an open network is created.
+         * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
+         * When set to null, an open network is created.
+         * <p>
          *
          * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
          *                   configuration.
@@ -222,10 +408,72 @@
         @NonNull
         public Builder setWpa2Passphrase(@Nullable String passphrase) {
             if (passphrase != null) {
+                final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+                if (!asciiEncoder.canEncode(passphrase)) {
+                    throw new IllegalArgumentException("passphrase not ASCII encodable");
+                }
                 Preconditions.checkStringNotEmpty(passphrase);
             }
+            clearAllPassphrase();
             mWpa2Passphrase = passphrase;
             return this;
         }
+
+        /**
+         * Specifies whether the AP is hidden (doesn't broadcast its SSID) or
+         * not (broadcasts its SSID).
+         * <p>
+         * <li>If not set, defaults to false (i.e not a hidden network).</li>
+         *
+         * @param hiddenSsid true for a hidden SSID, false otherwise.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder setHiddenSsid(boolean hiddenSsid) {
+            mHiddenSsid = hiddenSsid;
+            return this;
+        }
+
+        /**
+         * Specifies the band for the AP.
+         * <p>
+         * <li>If not set, defaults to BAND_2GHZ {@link @BandType}.</li>
+         *
+         * @param band One of the band types from {@link @BandType}.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder setBand(@BandType int band) {
+            switch (band) {
+                case BAND_2GHZ:
+                    break;
+                case BAND_5GHZ:
+                    break;
+                case BAND_ANY:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid band type");
+            }
+            mBand = band;
+            return this;
+        }
+
+        /**
+         * Specifies the channel for the AP.
+         *
+         * The channel which AP resides on. Valid channels are country dependent.
+         * Use the special channel value 0 to have the framework auto-select a valid channel
+         * from the band configured with {@link #setBand(@BandType int)}.
+         *
+         * <p>
+         * <li>If not set, defaults to 0.</li>
+         * @param channel operating channel of the AP.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder setChannel(int channel) {
+            mChannel = channel;
+            return this;
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 0f7fc2d..f0f31fa 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -16,10 +16,14 @@
 
 package android.net.wifi;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Record of energy and activity information from controller and
@@ -27,179 +31,197 @@
  * real-time.
  * @hide
  */
+@SystemApi
 public final class WifiActivityEnergyInfo implements Parcelable {
-    /**
-     * @hide
-     */
-    public long mTimestamp;
+    private long mTimeSinceBootMillis;
+    @StackState
+    private int mStackState;
+    private long mControllerTxDurationMillis;
+    private long mControllerRxDurationMillis;
+    private long mControllerScanDurationMillis;
+    private long mControllerIdleDurationMillis;
+    private long mControllerEnergyUsedMicroJoules;
 
-    /**
-     * @hide
-     */
-    public int mStackState;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"STACK_STATE_"}, value = {
+            STACK_STATE_INVALID,
+            STACK_STATE_STATE_ACTIVE,
+            STACK_STATE_STATE_SCANNING,
+            STACK_STATE_STATE_IDLE})
+    public @interface StackState {}
 
-    /**
-     * @hide
-     */
-    public long mControllerTxTimeMs;
-
-    /**
-     * @hide
-     */
-    public long[] mControllerTxTimePerLevelMs;
-
-    /**
-     * @hide
-     */
-    public long mControllerRxTimeMs;
-
-    /**
-     * @hide
-     */
-    public long mControllerScanTimeMs;
-
-    /**
-     * @hide
-     */
-    public long mControllerIdleTimeMs;
-
-    /**
-     * @hide
-     */
-    public long mControllerEnergyUsed;
-
+    /** Invalid Wifi stack state. */
     public static final int STACK_STATE_INVALID = 0;
+    /** Wifi stack is active. */
     public static final int STACK_STATE_STATE_ACTIVE = 1;
+    /** Wifi stack is scanning. */
     public static final int STACK_STATE_STATE_SCANNING = 2;
+    /** Wifi stack is idle. */
     public static final int STACK_STATE_STATE_IDLE = 3;
 
-    public WifiActivityEnergyInfo(long timestamp, int stackState,
-                                  long txTime, long[] txTimePerLevel, long rxTime, long scanTime,
-                                  long idleTime, long energyUsed) {
-        mTimestamp = timestamp;
+    /**
+     * Constructor.
+     *
+     * @param timeSinceBootMillis the time since boot, in milliseconds.
+     * @param stackState The current state of the Wifi Stack. One of {@link #STACK_STATE_INVALID},
+     *                   {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+     *                   or {@link #STACK_STATE_STATE_IDLE}.
+     * @param txDurationMillis Cumulative milliseconds of active transmission.
+     * @param rxDurationMillis Cumulative milliseconds of active receive.
+     * @param scanDurationMillis Cumulative milliseconds when radio is awake due to scan.
+     * @param idleDurationMillis Cumulative milliseconds when radio is awake but not transmitting or
+     *                       receiving.
+     * @param energyUsedMicroJoules Cumulative energy consumed by Wifi, in microjoules.
+     */
+    public WifiActivityEnergyInfo(
+            long timeSinceBootMillis,
+            @StackState int stackState,
+            long txDurationMillis,
+            long rxDurationMillis,
+            long scanDurationMillis,
+            long idleDurationMillis,
+            long energyUsedMicroJoules) {
+        mTimeSinceBootMillis = timeSinceBootMillis;
         mStackState = stackState;
-        mControllerTxTimeMs = txTime;
-        mControllerTxTimePerLevelMs = txTimePerLevel;
-        mControllerRxTimeMs = rxTime;
-        mControllerScanTimeMs = scanTime;
-        mControllerIdleTimeMs = idleTime;
-        mControllerEnergyUsed = energyUsed;
+        mControllerTxDurationMillis = txDurationMillis;
+        mControllerRxDurationMillis = rxDurationMillis;
+        mControllerScanDurationMillis = scanDurationMillis;
+        mControllerIdleDurationMillis = idleDurationMillis;
+        mControllerEnergyUsedMicroJoules = energyUsedMicroJoules;
     }
 
     @Override
     public String toString() {
         return "WifiActivityEnergyInfo{"
-            + " timestamp=" + mTimestamp
-            + " mStackState=" + mStackState
-            + " mControllerTxTimeMs=" + mControllerTxTimeMs
-            + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
-            + " mControllerRxTimeMs=" + mControllerRxTimeMs
-            + " mControllerScanTimeMs=" + mControllerScanTimeMs
-            + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
-            + " mControllerEnergyUsed=" + mControllerEnergyUsed
-            + " }";
+                + " mTimeSinceBootMillis=" + mTimeSinceBootMillis
+                + " mStackState=" + mStackState
+                + " mControllerTxDurationMillis=" + mControllerTxDurationMillis
+                + " mControllerRxDurationMillis=" + mControllerRxDurationMillis
+                + " mControllerScanDurationMillis=" + mControllerScanDurationMillis
+                + " mControllerIdleDurationMillis=" + mControllerIdleDurationMillis
+                + " mControllerEnergyUsedMicroJoules=" + mControllerEnergyUsedMicroJoules
+                + " }";
     }
 
-    public static final @android.annotation.NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR =
+    public static final @NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR =
             new Parcelable.Creator<WifiActivityEnergyInfo>() {
         public WifiActivityEnergyInfo createFromParcel(Parcel in) {
             long timestamp = in.readLong();
             int stackState = in.readInt();
             long txTime = in.readLong();
-            long[] txTimePerLevel = in.createLongArray();
             long rxTime = in.readLong();
             long scanTime = in.readLong();
             long idleTime = in.readLong();
             long energyUsed = in.readLong();
             return new WifiActivityEnergyInfo(timestamp, stackState,
-                    txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed);
+                    txTime, rxTime, scanTime, idleTime, energyUsed);
         }
         public WifiActivityEnergyInfo[] newArray(int size) {
             return new WifiActivityEnergyInfo[size];
         }
     };
 
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mTimestamp);
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeLong(mTimeSinceBootMillis);
         out.writeInt(mStackState);
-        out.writeLong(mControllerTxTimeMs);
-        out.writeLongArray(mControllerTxTimePerLevelMs);
-        out.writeLong(mControllerRxTimeMs);
-        out.writeLong(mControllerScanTimeMs);
-        out.writeLong(mControllerIdleTimeMs);
-        out.writeLong(mControllerEnergyUsed);
+        out.writeLong(mControllerTxDurationMillis);
+        out.writeLong(mControllerRxDurationMillis);
+        out.writeLong(mControllerScanDurationMillis);
+        out.writeLong(mControllerIdleDurationMillis);
+        out.writeLong(mControllerEnergyUsedMicroJoules);
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    /** Get the timestamp (milliseconds since boot) of record creation. */
+    public long getTimeSinceBootMillis() {
+        return mTimeSinceBootMillis;
+    }
+
+    /** Set the timestamp (milliseconds since boot) of record creation. */
+    public void setTimeSinceBootMillis(long timeSinceBootMillis) {
+        mTimeSinceBootMillis = timeSinceBootMillis;
+    }
+
     /**
-     * @return bt stack reported state
+     * Get the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
+     * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+     * {@link #STACK_STATE_STATE_IDLE}.
      */
+    @StackState
     public int getStackState() {
         return mStackState;
     }
 
     /**
-     * @return tx time in ms
+     * Set the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
+     * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+     * {@link #STACK_STATE_STATE_IDLE}.
      */
-    public long getControllerTxTimeMillis() {
-        return mControllerTxTimeMs;
+    public void setStackState(@StackState int stackState) {
+        mStackState = stackState;
     }
 
-    /**
-     * @return tx time at power level provided in ms
-     */
-    public long getControllerTxTimeMillisAtLevel(int level) {
-        if (level < mControllerTxTimePerLevelMs.length) {
-            return mControllerTxTimePerLevelMs[level];
-        }
-        return 0;
+    /** Get the Wifi transmission duration, in milliseconds. */
+    public long getControllerTxDurationMillis() {
+        return mControllerTxDurationMillis;
     }
 
-    /**
-     * @return rx time in ms
-     */
-    public long getControllerRxTimeMillis() {
-        return mControllerRxTimeMs;
+    /** Set the Wifi transmission duration, in milliseconds. */
+    public void setControllerTxDurationMillis(long controllerTxDurationMillis) {
+        mControllerTxDurationMillis = controllerTxDurationMillis;
     }
 
-    /**
-     * @return scan time in ms
-     */
-    public long getControllerScanTimeMillis() {
-        return mControllerScanTimeMs;
+    /** Get the Wifi receive duration, in milliseconds. */
+    public long getControllerRxDurationMillis() {
+        return mControllerRxDurationMillis;
     }
 
-    /**
-     * @return idle time in ms
-     */
-    public long getControllerIdleTimeMillis() {
-        return mControllerIdleTimeMs;
+    /** Set the Wifi receive duration, in milliseconds. */
+    public void setControllerRxDurationMillis(long controllerRxDurationMillis) {
+        mControllerRxDurationMillis = controllerRxDurationMillis;
     }
 
-    /**
-     * product of current(mA), voltage(V) and time(ms)
-     * @return energy used
-     */
-    public long getControllerEnergyUsed() {
-        return mControllerEnergyUsed;
-    }
-    /**
-     * @return timestamp(wall clock) of record creation
-     */
-    public long getTimeStamp() {
-        return mTimestamp;
+    /** Get the Wifi scan duration, in milliseconds. */
+    public long getControllerScanDurationMillis() {
+        return mControllerScanDurationMillis;
     }
 
-    /**
-     * @return if the record is valid
-     */
+    /** Set the Wifi scan duration, in milliseconds. */
+    public void setControllerScanDurationMillis(long controllerScanDurationMillis) {
+        mControllerScanDurationMillis = controllerScanDurationMillis;
+    }
+
+    /** Get the Wifi idle duration, in milliseconds. */
+    public long getControllerIdleDurationMillis() {
+        return mControllerIdleDurationMillis;
+    }
+
+    /** Set the Wifi idle duration, in milliseconds. */
+    public void setControllerIdleDurationMillis(long controllerIdleDurationMillis) {
+        mControllerIdleDurationMillis = controllerIdleDurationMillis;
+    }
+
+    /** Get the energy consumed by Wifi, in microjoules. */
+    public long getControllerEnergyUsedMicroJoules() {
+        return mControllerEnergyUsedMicroJoules;
+    }
+
+    /** Set the energy consumed by Wifi, in microjoules. */
+    public void setControllerEnergyUsedMicroJoules(long controllerEnergyUsedMicroJoules) {
+        mControllerEnergyUsedMicroJoules = controllerEnergyUsedMicroJoules;
+    }
+
+    /** Returns true if the record is valid, false otherwise. */
     public boolean isValid() {
-        return ((mControllerTxTimeMs >=0) &&
-                (mControllerRxTimeMs >=0) &&
-                (mControllerScanTimeMs >=0) &&
-                (mControllerIdleTimeMs >=0));
+        return mControllerTxDurationMillis >= 0
+                && mControllerRxDurationMillis >= 0
+                && mControllerScanDurationMillis >= 0
+                && mControllerIdleDurationMillis >= 0;
     }
-}
\ No newline at end of file
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e08a11e..d068fc6 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -39,7 +39,6 @@
 import android.util.BackupUtils;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -49,6 +48,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.BitSet;
+import java.util.Calendar;
 import java.util.HashMap;
 
 /**
@@ -2006,6 +2006,16 @@
                 && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
 
+    private static String logTimeOfDay(long millis) {
+        Calendar c = Calendar.getInstance();
+        if (millis >= 0) {
+            c.setTimeInMillis(millis);
+            return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+        } else {
+            return Long.toString(millis);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sbuf = new StringBuilder();
@@ -2042,7 +2052,7 @@
         if (mNetworkSelectionStatus.getConnectChoice() != null) {
             sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
             sbuf.append(" connect choice set time: ")
-                    .append(TimeUtils.logTimeOfDay(
+                    .append(logTimeOfDay(
                             mNetworkSelectionStatus.getConnectChoiceTimestamp()));
         }
         sbuf.append(" hasEverConnected: ")
@@ -2081,7 +2091,7 @@
         sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n");
         sbuf.append(" randomizedMacExpirationTimeMs: ")
                 .append(randomizedMacExpirationTimeMs == 0 ? "<none>"
-                        : TimeUtils.logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
+                        : logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
         sbuf.append(" KeyMgmt:");
         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
             if (this.allowedKeyManagement.get(k)) {
@@ -2205,7 +2215,7 @@
 
         if (this.lastConnected != 0) {
             sbuf.append('\n');
-            sbuf.append("lastConnected: ").append(TimeUtils.logTimeOfDay(this.lastConnected));
+            sbuf.append("lastConnected: ").append(logTimeOfDay(this.lastConnected));
             sbuf.append(" ");
         }
         sbuf.append('\n');
@@ -2355,7 +2365,7 @@
                 ? getSsidAndSecurityTypeString()
                 : FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
         if (!shared) {
-            key += "-" + UserHandle.getUserId(creatorUid);
+            key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier();
         }
         return key;
     }
@@ -2426,13 +2436,13 @@
     @NonNull
     @SystemApi
     public IpConfiguration.IpAssignment getIpAssignment() {
-        return mIpConfiguration.ipAssignment;
+        return mIpConfiguration.getIpAssignment();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
-        mIpConfiguration.ipAssignment = ipAssignment;
+        mIpConfiguration.setIpAssignment(ipAssignment);
     }
 
     /**
@@ -2442,13 +2452,13 @@
     @NonNull
     @SystemApi
     public IpConfiguration.ProxySettings getProxySettings() {
-        return mIpConfiguration.proxySettings;
+        return mIpConfiguration.getProxySettings();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
-        mIpConfiguration.proxySettings = proxySettings;
+        mIpConfiguration.setProxySettings(proxySettings);
     }
 
     /**
@@ -2457,10 +2467,10 @@
      *                  WifiConfiguration, or {@code null} if no proxy is specified.
      */
     public ProxyInfo getHttpProxy() {
-        if (mIpConfiguration.proxySettings == IpConfiguration.ProxySettings.NONE) {
+        if (mIpConfiguration.getProxySettings() == IpConfiguration.ProxySettings.NONE) {
             return null;
         }
-        return new ProxyInfo(mIpConfiguration.httpProxy);
+        return new ProxyInfo(mIpConfiguration.getHttpProxy());
     }
 
     /**
@@ -2485,12 +2495,12 @@
         if (!Uri.EMPTY.equals(httpProxy.getPacFileUrl())) {
             proxySettingCopy = IpConfiguration.ProxySettings.PAC;
             // Construct a new PAC URL Proxy
-            httpProxyCopy = new ProxyInfo(httpProxy.getPacFileUrl(), httpProxy.getPort());
+            httpProxyCopy = ProxyInfo.buildPacProxy(httpProxy.getPacFileUrl(), httpProxy.getPort());
         } else {
             proxySettingCopy = IpConfiguration.ProxySettings.STATIC;
             // Construct a new HTTP Proxy
-            httpProxyCopy = new ProxyInfo(httpProxy.getHost(), httpProxy.getPort(),
-                    httpProxy.getExclusionListAsString());
+            httpProxyCopy = ProxyInfo.buildDirectProxy(httpProxy.getHost(), httpProxy.getPort(),
+                    Arrays.asList(httpProxy.getExclusionList()));
         }
         if (!httpProxyCopy.isValid()) {
             throw new IllegalArgumentException("Invalid ProxyInfo: " + httpProxyCopy.toString());
@@ -2505,8 +2515,8 @@
      */
     @SystemApi
     public void setProxy(@NonNull ProxySettings settings, @NonNull ProxyInfo proxy) {
-        mIpConfiguration.proxySettings = settings;
-        mIpConfiguration.httpProxy = proxy;
+        mIpConfiguration.setProxySettings(settings);
+        mIpConfiguration.setHttpProxy(proxy);
     }
 
     /** Implement the Parcelable interface {@hide} */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6557bbe..52f5eee 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -36,15 +36,12 @@
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
 import android.net.NetworkStack;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.IBinder;
@@ -57,7 +54,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.net.NetworkPinner;
 
 import dalvik.system.CloseGuard;
 
@@ -150,12 +146,11 @@
     @Deprecated
     public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
 
-    /**
-     * Maximum number of active network suggestions allowed per app.
-     * @hide
-     */
-    public static final int NETWORK_SUGGESTIONS_MAX_PER_APP =
-            ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
+    /** @hide */
+    public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM = 256;
+
+    /** @hide */
+    public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM = 1024;
 
     /**
      * Reason code if all of the network suggestions were successfully added or removed.
@@ -728,7 +723,9 @@
      * had been reset.
      * @hide
      */
-    public static final String WIFI_NETWORK_SETTINGS_RESET_ACTION =
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING)
+    public static final String ACTION_NETWORK_SETTINGS_RESET =
             "android.net.wifi.action.NETWORK_SETTINGS_RESET";
 
     /**
@@ -1834,7 +1831,15 @@
      * @see #removeNetworkSuggestions(List)
      */
     public int getMaxNumberOfNetworkSuggestionsPerApp() {
-        return NETWORK_SUGGESTIONS_MAX_PER_APP;
+        return getMaxNumberOfNetworkSuggestionsPerApp(
+                mContext.getSystemService(ActivityManager.class).isLowRamDevice());
+    }
+
+    /** @hide */
+    public static int getMaxNumberOfNetworkSuggestionsPerApp(boolean isLowRamDevice) {
+        return isLowRamDevice
+                ? NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM
+                : NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM;
     }
 
     /**
@@ -2022,28 +2027,11 @@
      */
     @Deprecated
     public boolean enableNetwork(int netId, boolean attemptConnect) {
-        final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP;
-        if (pin) {
-            NetworkRequest request = new NetworkRequest.Builder()
-                    .clearCapabilities()
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                    .build();
-            NetworkPinner.pin(mContext, request);
-        }
-
-        boolean success;
         try {
-            success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
+            return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
-        if (pin && !success) {
-            NetworkPinner.unpin();
-        }
-
-        return success;
     }
 
     /**
@@ -2760,7 +2748,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_STACK,
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
@@ -2774,6 +2761,31 @@
     }
 
     /**
+     * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
+     * Note that starting Soft AP mode may disable station mode operation if the device does not
+     * support concurrency.
+     * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP,
+     *                     or null to use the persisted Soft AP configuration that was previously
+     *                     set using {@link #setSoftApConfiguration(softApConfiguration)}.
+     * @return {@code true} if the operation succeeded, {@code false} otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
+    public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {
+        try {
+            return mService.startTetheredHotspot(softApConfig);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
      * Stop SoftAp mode.
      * Note that stopping softap mode will restore the previous wifi mode.
      * @return {@code true} if the operation succeeds, {@code false} otherwise
@@ -3057,10 +3069,12 @@
      * Gets the Wi-Fi AP Configuration.
      * @return AP details in WifiConfiguration
      *
+     * @deprecated This API is deprecated. Use {@link #getSoftApConfiguration()} instead.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+    @Deprecated
     public WifiConfiguration getWifiApConfiguration() {
         try {
             return mService.getWifiApConfiguration();
@@ -3070,17 +3084,56 @@
     }
 
     /**
-     * Sets the Wi-Fi AP Configuration.  The AP configuration must either be open or
-     * WPA2 PSK networks.
+     * Gets the Wi-Fi AP Configuration.
+     * @return AP details in {@link SoftApConfiguration}
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+    public SoftApConfiguration getSoftApConfiguration() {
+        try {
+            return mService.getSoftApConfiguration();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the Wi-Fi AP Configuration.
+     * @return {@code true} if the operation succeeded, {@code false} otherwise
+     *
+     * @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)}
+     * instead.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
+    @Deprecated
+    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+        try {
+            return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the Wi-Fi AP Configuration.
+     *
+     * @param softApConfig  A valid SoftApConfiguration specifying the configuration of the SAP.
+
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
-    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public boolean setSoftApConfiguration(@NonNull SoftApConfiguration softApConfig) {
         try {
-            return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+            return mService.setSoftApConfiguration(
+                    softApConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4569,6 +4622,36 @@
     }
 
     /**
+     * Retrieve the soft ap config data to be backed to save current config data.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public byte[] retrieveSoftApBackupData() {
+        try {
+            return mService.retrieveSoftApBackupData();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Restore soft ap config from the backed up data.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void restoreSoftApBackupData(@NonNull byte[] data) {
+        try {
+            mService.restoreSoftApBackupData(data);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Restore state from the older version of back up data.
      * The old backup data was essentially a backup of wpa_supplicant.conf
      * and ipconfig.txt file.
@@ -5105,9 +5188,9 @@
                                 Log.v(TAG, "OnWifiUsabilityStatsListener: "
                                         + "onWifiUsabilityStats: seqNum=" + seqNum);
                             }
-                            Binder.withCleanCallingIdentity(() ->
-                                    executor.execute(() -> listener.onWifiUsabilityStats(seqNum,
-                                            isSameBssidAndFreq, stats)));
+                            Binder.clearCallingIdentity();
+                            executor.execute(() -> listener.onWifiUsabilityStats(
+                                    seqNum, isSameBssidAndFreq, stats));
                         }
                     },
                     listener.hashCode()
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 77f7b9e..b293077 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -59,6 +59,60 @@
 public class WifiScanner {
 
     /** @hide */
+    public static final int WIFI_BAND_INDEX_24_GHZ = 0;
+    /** @hide */
+    public static final int WIFI_BAND_INDEX_5_GHZ = 1;
+    /** @hide */
+    public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2;
+    /** @hide */
+    public static final int WIFI_BAND_COUNT = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = {
+            WIFI_BAND_INDEX_24_GHZ,
+            WIFI_BAND_INDEX_5_GHZ,
+            WIFI_BAND_INDEX_5_GHZ_DFS_ONLY})
+    public @interface WifiBandIndex {}
+
+    /** no band specified; use channel list instead */
+    public static final int WIFI_BAND_UNSPECIFIED = 0;
+    /** 2.4 GHz band */
+    public static final int WIFI_BAND_24_GHZ = 1 << WIFI_BAND_INDEX_24_GHZ;
+    /** 5 GHz band excluding DFS channels */
+    public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ;
+    /** DFS channels from 5 GHz band only */
+    public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"WIFI_BAND_"}, value = {
+            WIFI_BAND_UNSPECIFIED,
+            WIFI_BAND_24_GHZ,
+            WIFI_BAND_5_GHZ,
+            WIFI_BAND_5_GHZ_DFS_ONLY})
+    public @interface WifiBandBasic {}
+
+    /**
+     * Combination of bands
+     * Note that those are only the common band combinations,
+     * other combinations can be created by combining any of the basic bands above
+     */
+    /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
+    public static final int WIFI_BAND_BOTH = WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ;
+    /**
+     * 2.4Ghz band + DFS channels from 5 GHz band only
+     * @hide
+     */
+    public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS  =
+            WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+    /** 5 GHz band including DFS channels */
+    public static final int WIFI_BAND_5_GHZ_WITH_DFS  = WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+    /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
+    public static final int WIFI_BAND_BOTH_WITH_DFS =
+            WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"WIFI_BAND_"}, value = {
             WIFI_BAND_UNSPECIFIED,
@@ -71,25 +125,6 @@
             WIFI_BAND_BOTH_WITH_DFS})
     public @interface WifiBand {}
 
-    /** no band specified; use channel list instead */
-    public static final int WIFI_BAND_UNSPECIFIED = 0;
-    /** 2.4 GHz band */
-    public static final int WIFI_BAND_24_GHZ = 1;
-    /** 5 GHz band excluding DFS channels */
-    public static final int WIFI_BAND_5_GHZ = 2;
-    /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
-    public static final int WIFI_BAND_BOTH = 3;
-    /** DFS channels from 5 GHz band only */
-    public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 4;
-    /**
-     * 2.4Ghz band + DFS channels from 5 GHz band only
-     * @hide
-     */
-    public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS  = 5;
-    /** 5 GHz band including DFS channels */
-    public static final int WIFI_BAND_5_GHZ_WITH_DFS  = 6;
-    /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
-    public static final int WIFI_BAND_BOTH_WITH_DFS = 7;
     /**
      * Max band value
      * @hide
@@ -398,7 +433,6 @@
                         return new ScanSettings[size];
                     }
                 };
-
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index b3b5b29..2d3cc1e 100644
--- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -18,8 +18,6 @@
 
 import android.annotation.Nullable;
 
-import libcore.io.Memory;
-
 import java.nio.BufferOverflowException;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
@@ -266,7 +264,7 @@
         public TlvConstructor putShort(int type, short data) {
             checkLength(2);
             addHeader(type, 2);
-            Memory.pokeShort(mArray, mPosition, data, mByteOrder);
+            pokeShort(mArray, mPosition, data, mByteOrder);
             mPosition += 2;
             return this;
         }
@@ -284,7 +282,7 @@
         public TlvConstructor putInt(int type, int data) {
             checkLength(4);
             addHeader(type, 4);
-            Memory.pokeInt(mArray, mPosition, data, mByteOrder);
+            pokeInt(mArray, mPosition, data, mByteOrder);
             mPosition += 4;
             return this;
         }
@@ -349,14 +347,14 @@
             if (mTypeSize == 1) {
                 mArray[mPosition] = (byte) type;
             } else if (mTypeSize == 2) {
-                Memory.pokeShort(mArray, mPosition, (short) type, mByteOrder);
+                pokeShort(mArray, mPosition, (short) type, mByteOrder);
             }
             mPosition += mTypeSize;
 
             if (mLengthSize == 1) {
                 mArray[mPosition] = (byte) length;
             } else if (mLengthSize == 2) {
-                Memory.pokeShort(mArray, mPosition, (short) length, mByteOrder);
+                pokeShort(mArray, mPosition, (short) length, mByteOrder);
             }
             mPosition += mLengthSize;
         }
@@ -445,7 +443,7 @@
                 throw new IllegalArgumentException(
                         "Accesing a short from a TLV element of length " + length);
             }
-            return Memory.peekShort(mRefArray, offset, byteOrder);
+            return peekShort(mRefArray, offset, byteOrder);
         }
 
         /**
@@ -460,7 +458,7 @@
                 throw new IllegalArgumentException(
                         "Accesing an int from a TLV element of length " + length);
             }
-            return Memory.peekInt(mRefArray, offset, byteOrder);
+            return peekInt(mRefArray, offset, byteOrder);
         }
 
         /**
@@ -590,7 +588,7 @@
                     if (mTypeSize == 1) {
                         type = mArray[mOffset];
                     } else if (mTypeSize == 2) {
-                        type = Memory.peekShort(mArray, mOffset, mByteOrder);
+                        type = peekShort(mArray, mOffset, mByteOrder);
                     }
                     mOffset += mTypeSize;
 
@@ -598,7 +596,7 @@
                     if (mLengthSize == 1) {
                         length = mArray[mOffset];
                     } else if (mLengthSize == 2) {
-                        length = Memory.peekShort(mArray, mOffset, mByteOrder);
+                        length = peekShort(mArray, mOffset, mByteOrder);
                     }
                     mOffset += mLengthSize;
 
@@ -661,10 +659,56 @@
             if (lengthSize == 1) {
                 nextTlvIndex += lengthSize + array[nextTlvIndex];
             } else {
-                nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex, byteOrder);
+                nextTlvIndex += lengthSize + peekShort(array, nextTlvIndex, byteOrder);
             }
         }
 
         return nextTlvIndex == array.length;
     }
+
+    private static void pokeShort(byte[] dst, int offset, short value, ByteOrder order) {
+        if (order == ByteOrder.BIG_ENDIAN) {
+            dst[offset++] = (byte) ((value >> 8) & 0xff);
+            dst[offset  ] = (byte) ((value >> 0) & 0xff);
+        } else {
+            dst[offset++] = (byte) ((value >> 0) & 0xff);
+            dst[offset  ] = (byte) ((value >> 8) & 0xff);
+        }
+    }
+
+    private static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) {
+        if (order == ByteOrder.BIG_ENDIAN) {
+            dst[offset++] = (byte) ((value >> 24) & 0xff);
+            dst[offset++] = (byte) ((value >> 16) & 0xff);
+            dst[offset++] = (byte) ((value >>  8) & 0xff);
+            dst[offset  ] = (byte) ((value >>  0) & 0xff);
+        } else {
+            dst[offset++] = (byte) ((value >>  0) & 0xff);
+            dst[offset++] = (byte) ((value >>  8) & 0xff);
+            dst[offset++] = (byte) ((value >> 16) & 0xff);
+            dst[offset  ] = (byte) ((value >> 24) & 0xff);
+        }
+    }
+
+    private static short peekShort(byte[] src, int offset, ByteOrder order) {
+        if (order == ByteOrder.BIG_ENDIAN) {
+            return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff));
+        } else {
+            return (short) ((src[offset + 1] << 8) | (src[offset] & 0xff));
+        }
+    }
+
+    private static int peekInt(byte[] src, int offset, ByteOrder order) {
+        if (order == ByteOrder.BIG_ENDIAN) {
+            return ((src[offset++] & 0xff) << 24)
+                    | ((src[offset++] & 0xff) << 16)
+                    | ((src[offset++] & 0xff) <<  8)
+                    | ((src[offset  ] & 0xff) <<  0);
+        } else {
+            return ((src[offset++] & 0xff) <<  0)
+                    | ((src[offset++] & 0xff) <<  8)
+                    | ((src[offset++] & 0xff) << 16)
+                    | ((src[offset  ] & 0xff) << 24);
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 9164d04..5ec4c8b 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -207,14 +207,14 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeBlob(mData);
+            dest.writeByteArray(mData);
         }
 
         public static final @android.annotation.NonNull Creator<ByteArrayWrapper> CREATOR =
                 new Creator<ByteArrayWrapper>() {
                     @Override
                     public ByteArrayWrapper createFromParcel(Parcel in) {
-                        return new ByteArrayWrapper(in.readBlob());
+                        return new ByteArrayWrapper(in.createByteArray());
                     }
 
                     @Override
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 3bef502..f0a0607 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiSsid;
 import android.os.Bundle;
@@ -86,23 +85,16 @@
      */
     private final List<Integer> mMethodList;
 
-    /**
-     * Icon data for the OSU (Online Sign-Up) provider.
-     */
-    private final Icon mIcon;
-
     /** @hide */
     public OsuProvider(String osuSsid, Map<String, String> friendlyNames,
-            String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
-            Icon icon) {
+            String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) {
         this(WifiSsid.createFromByteArray(osuSsid.getBytes(StandardCharsets.UTF_8)),
-                friendlyNames, serviceDescription, serverUri, nai, methodList, icon);
+                friendlyNames, serviceDescription, serverUri, nai, methodList);
     }
 
     /** @hide */
     public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames,
-            String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
-            Icon icon) {
+            String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) {
         mOsuSsid = osuSsid;
         mFriendlyNames = friendlyNames;
         mServiceDescription = serviceDescription;
@@ -113,7 +105,6 @@
         } else {
             mMethodList = new ArrayList<>(methodList);
         }
-        mIcon = icon;
     }
 
     /**
@@ -130,7 +121,6 @@
             mServerUri = null;
             mNetworkAccessIdentifier = null;
             mMethodList = new ArrayList<>();
-            mIcon = null;
             return;
         }
 
@@ -144,7 +134,6 @@
         } else {
             mMethodList = new ArrayList<>(source.mMethodList);
         }
-        mIcon = source.mIcon;
     }
 
     /** @hide */
@@ -205,11 +194,6 @@
         return mMethodList;
     }
 
-    /** @hide */
-    public Icon getIcon() {
-        return mIcon;
-    }
-
     @Override
     public int describeContents() {
         return 0;
@@ -222,7 +206,6 @@
         dest.writeParcelable(mServerUri, flags);
         dest.writeString(mNetworkAccessIdentifier);
         dest.writeList(mMethodList);
-        dest.writeParcelable(mIcon, flags);
         Bundle bundle = new Bundle();
         bundle.putSerializable("friendlyNameMap", (HashMap<String, String>) mFriendlyNames);
         dest.writeBundle(bundle);
@@ -237,21 +220,16 @@
             return false;
         }
         OsuProvider that = (OsuProvider) thatObject;
-        return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid))
-                && (mFriendlyNames == null) ? that.mFriendlyNames == null
-                            : mFriendlyNames.equals(that.mFriendlyNames)
+        return Objects.equals(mOsuSsid, that.mOsuSsid)
+                && Objects.equals(mFriendlyNames, that.mFriendlyNames)
                 && TextUtils.equals(mServiceDescription, that.mServiceDescription)
-                && (mServerUri == null ? that.mServerUri == null
-                            : mServerUri.equals(that.mServerUri))
+                && Objects.equals(mServerUri, that.mServerUri)
                 && TextUtils.equals(mNetworkAccessIdentifier, that.mNetworkAccessIdentifier)
-                && (mMethodList == null ? that.mMethodList == null
-                            : mMethodList.equals(that.mMethodList))
-                && (mIcon == null ? that.mIcon == null : mIcon.sameAs(that.mIcon));
+                && Objects.equals(mMethodList, that.mMethodList);
     }
 
     @Override
     public int hashCode() {
-        // mIcon is not hashable, skip the variable.
         return Objects.hash(mOsuSsid, mServiceDescription, mFriendlyNames,
                 mServerUri, mNetworkAccessIdentifier, mMethodList);
     }
@@ -264,8 +242,7 @@
                 + " mServiceDescription=" + mServiceDescription
                 + " mServerUri=" + mServerUri
                 + " mNetworkAccessIdentifier=" + mNetworkAccessIdentifier
-                + " mMethodList=" + mMethodList
-                + " mIcon=" + mIcon;
+                + " mMethodList=" + mMethodList;
     }
 
     public static final @android.annotation.NonNull Creator<OsuProvider> CREATOR =
@@ -278,12 +255,11 @@
                     String nai = in.readString();
                     List<Integer> methodList = new ArrayList<>();
                     in.readList(methodList, null);
-                    Icon icon = in.readParcelable(null);
                     Bundle bundle = in.readBundle();
                     Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
                             "friendlyNameMap");
                     return new OsuProvider(osuSsid, friendlyNamesMap, serviceDescription,
-                            serverUri, nai, methodList, icon);
+                            serverUri, nai, methodList);
                 }
 
             @Override
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
index e639282..4a32f52 100644
--- a/wifi/java/android/net/wifi/rtt/package.html
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -37,5 +37,9 @@
 <pre>
     getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
 </pre>
+
+<p>For an example of this functionality, see
+<a href="{@docRoot}guide/topics/connectivity/wifi-rtt" class="external">Wi-Fi location: ranging
+with RTT</a>.</p>
 </BODY>
 </HTML>
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 534e609..bc86839 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -285,6 +285,11 @@
     }
 
     @Override
+    public boolean startTetheredHotspot(SoftApConfiguration softApConfig) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean stopSoftAp() {
         throw new UnsupportedOperationException();
     }
@@ -321,10 +326,21 @@
     }
 
     @Override
+    public SoftApConfiguration getSoftApConfiguration() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void notifyUserOfApBandConversion(String packageName) {
         throw new UnsupportedOperationException();
     }
@@ -385,6 +401,16 @@
     }
 
     @Override
+    public byte[] retrieveSoftApBackupData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void restoreSoftApBackupData(byte[] data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 949b479..b8d3e41 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -46,6 +46,10 @@
         assertThat(original.getSsid()).isEqualTo("ssid");
         assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
         assertThat(original.getWpa2Passphrase()).isNull();
+        assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+        assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
+        assertThat(original.getChannel()).isEqualTo(0);
+        assertThat(original.isHiddenSsid()).isEqualTo(false);
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
         assertThat(unparceled).isNotSameAs(original);
@@ -64,6 +68,39 @@
                 .setWpa2Passphrase("secretsecret")
                 .build();
         assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+        assertThat(original.getSecurityType()).isEqualTo(
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
+        assertThat(original.getChannel()).isEqualTo(0);
+        assertThat(original.isHiddenSsid()).isEqualTo(false);
+
+
+        SoftApConfiguration unparceled = parcelUnparcel(original);
+        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isEqualTo(original);
+        assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+        SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isEqualTo(original);
+        assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+    }
+
+    @Test
+    public void testWpa2WithBandAndChannelAndHiddenNetwork() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setWpa2Passphrase("secretsecret")
+                .setBand(SoftApConfiguration.BAND_ANY)
+                .setChannel(149)
+                .setHiddenSsid(true)
+                .build();
+        assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+        assertThat(original.getSecurityType()).isEqualTo(
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_ANY);
+        assertThat(original.getChannel()).isEqualTo(149);
+        assertThat(original.isHiddenSsid()).isEqualTo(true);
+
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
         assertThat(unparceled).isNotSameAs(original);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index d326201..b3a73bc 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -50,6 +50,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.net.DhcpInfo;
@@ -116,6 +117,7 @@
     @Mock Runnable mRunnable;
     @Mock Executor mExecutor;
     @Mock Executor mAnotherExecutor;
+    @Mock ActivityManager mActivityManager;
 
     private Handler mHandler;
     private TestLooper mLooper;
@@ -181,6 +183,33 @@
     }
 
     /**
+     * Check the call to startSoftAp calls WifiService to startSoftAp with the provided
+     * WifiConfiguration.  Verify that the return value is propagated to the caller.
+     */
+    @Test
+    public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception {
+        SoftApConfiguration mSoftApConfig = new SoftApConfiguration.Builder().build();
+        when(mWifiService.startTetheredHotspot(eq(mSoftApConfig))).thenReturn(true);
+        assertTrue(mWifiManager.startTetheredHotspot(mSoftApConfig));
+
+        when(mWifiService.startTetheredHotspot(eq(mSoftApConfig))).thenReturn(false);
+        assertFalse(mWifiManager.startTetheredHotspot(mSoftApConfig));
+    }
+
+    /**
+     * Check the call to startSoftAp calls WifiService to startSoftAp with a null config.  Verify
+     * that the return value is propagated to the caller.
+     */
+    @Test
+    public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception {
+        when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true);
+        assertTrue(mWifiManager.startTetheredHotspot(null));
+
+        when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false);
+        assertFalse(mWifiManager.startTetheredHotspot(null));
+    }
+
+    /**
      * Test creation of a LocalOnlyHotspotReservation and verify that close properly calls
      * WifiService.stopLocalOnlyHotspot.
      */
@@ -1148,6 +1177,43 @@
     }
 
     /**
+     * Verify that a successful call properly returns true.
+     */
+    @Test
+    public void testSetSoftApConfigurationSuccessReturnsTrue() throws Exception {
+        SoftApConfiguration apConfig = new SoftApConfiguration.Builder().build();
+
+        when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+                .thenReturn(true);
+        assertTrue(mWifiManager.setSoftApConfiguration(apConfig));
+    }
+
+    /**
+     * Verify that a failed call properly returns false.
+     */
+    @Test
+    public void testSetSoftApConfigurationFailureReturnsFalse() throws Exception {
+        SoftApConfiguration apConfig = new SoftApConfiguration.Builder().build();
+
+        when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+                .thenReturn(false);
+        assertFalse(mWifiManager.setSoftApConfiguration(apConfig));
+    }
+
+    /**
+     * Verify Exceptions are rethrown when underlying calls to WifiService throw exceptions.
+     */
+    @Test
+    public void testSetSoftApConfigurationRethrowsException() throws Exception {
+        doThrow(new SecurityException()).when(mWifiService).setSoftApConfiguration(any(), any());
+
+        try {
+            mWifiManager.setSoftApConfiguration(new SoftApConfiguration.Builder().build());
+            fail("setWifiApConfiguration should rethrow Exceptions from WifiService");
+        } catch (SecurityException e) { }
+    }
+
+    /**
      * Check the call to startScan calls WifiService.
      */
     @Test
@@ -1395,8 +1461,15 @@
      */
     @Test
     public void getMaxNumberOfNetworkSuggestionsPerApp() {
-        assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
-                mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+        when(mContext.getSystemServiceName(ActivityManager.class))
+                .thenReturn(Context.ACTIVITY_SERVICE);
+        when(mContext.getSystemService(Context.ACTIVITY_SERVICE))
+                .thenReturn(mActivityManager);
+        when(mActivityManager.isLowRamDevice()).thenReturn(true);
+        assertEquals(256, mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+
+        when(mActivityManager.isLowRamDevice()).thenReturn(false);
+        assertEquals(1024, mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
     }
 
     /**
@@ -1620,7 +1693,7 @@
     @Test
     public void testGetControllerActivityEnergyInfo() throws Exception {
         WifiActivityEnergyInfo activityEnergyInfo =
-                new WifiActivityEnergyInfo(5, 3, 3, new long[]{5L, 5L, 5L}, 5, 5, 5, 5);
+                new WifiActivityEnergyInfo(5, 3, 3, 5, 5, 5, 5);
         when(mWifiService.reportActivityInfo()).thenReturn(activityEnergyInfo);
 
         assertEquals(activityEnergyInfo, mWifiManager.getControllerActivityEnergyInfo());
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index 43ee249..2ded849 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiSsid;
 import android.os.Parcel;
@@ -56,7 +55,6 @@
     private static final String TEST_NAI = "test.access.com";
     private static final List<Integer> TEST_METHOD_LIST =
             Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP);
-    private static final Icon TEST_ICON = Icon.createWithData(new byte[10], 0, 10);
 
     /**
      * Verify parcel write and read consistency for the given {@link OsuProvider}.
@@ -82,7 +80,7 @@
      */
     @Test
     public void verifyParcelWithEmptyProviderInfo() throws Exception {
-        verifyParcel(new OsuProvider((WifiSsid) null, null, null, null, null, null, null));
+        verifyParcel(new OsuProvider((WifiSsid) null, null, null, null, null, null));
     }
 
     /**
@@ -93,7 +91,7 @@
     @Test
     public void verifyParcelWithFullProviderInfo() throws Exception {
         verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
-                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
+                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST));
     }
 
     /**
@@ -102,7 +100,7 @@
      */
     @Test
     public void verifyCopyConstructorWithNullSource() throws Exception {
-        OsuProvider expected = new OsuProvider((WifiSsid) null, null, null, null, null, null, null);
+        OsuProvider expected = new OsuProvider((WifiSsid) null, null, null, null, null, null);
         assertEquals(expected, new OsuProvider(null));
     }
 
@@ -114,7 +112,7 @@
     @Test
     public void verifyCopyConstructorWithValidSource() throws Exception {
         OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
-                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST);
         assertEquals(source, new OsuProvider(source));
     }
 
@@ -126,7 +124,7 @@
     @Test
     public void verifyGetters() throws Exception {
         OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
-                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST);
 
         assertTrue(TEST_SSID.equals(provider.getOsuSsid()));
         assertTrue(TEST_FRIENDLY_NAME.equals(provider.getFriendlyName()));
@@ -135,6 +133,5 @@
         assertTrue(TEST_SERVER_URI.equals(provider.getServerUri()));
         assertTrue(TEST_NAI.equals(provider.getNetworkAccessIdentifier()));
         assertTrue(TEST_METHOD_LIST.equals(provider.getMethodList()));
-        assertTrue(TEST_ICON.sameAs(provider.getIcon()));
     }
 }