Merge "Use the image of the person or large image for bubble experiment"
diff --git a/Android.bp b/Android.bp
index a587372..a7ce917 100644
--- a/Android.bp
+++ b/Android.bp
@@ -327,6 +327,7 @@
         "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",
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/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/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/api/current.txt b/api/current.txt
index 09006a5..bab334b 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();
@@ -52811,6 +52818,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";
@@ -52878,6 +52886,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;
diff --git a/api/system-current.txt b/api/system-current.txt
index 189585e..7f0050c 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";
@@ -4342,7 +4344,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);
@@ -5521,6 +5523,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, 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";
@@ -6047,6 +6050,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);
   }
 
@@ -6492,6 +6496,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);
@@ -7026,6 +7031,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";
diff --git a/api/test-current.txt b/api/test-current.txt
index 9ac789e..cf93410 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
   }
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/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/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/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/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/UserManager.java b/core/java/android/os/UserManager.java
index 74340f0..dbe8dc3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2709,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
@@ -2724,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.
@@ -3263,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/provider/Settings.java b/core/java/android/provider/Settings.java
index f19ef65..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>
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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 1855a26..db6211d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -162,7 +162,7 @@
                                                                  int[] allowedConfigs);
     private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken);
     private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
-            int defaultModeId, float minRefreshRate, float maxRefreshRate);
+            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
     private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
             IBinder displayToken);
@@ -1492,16 +1492,47 @@
     }
 
     /**
+     * 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,
-            int defaultModeId, float minRefreshRate, float maxRefreshRate) {
+            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
 
-        return nativeSetDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
-            maxRefreshRate);
+        return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
     }
 
     /**
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/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_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 bd202c1..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,13 +818,20 @@
     return allowedConfigsArray;
 }
 
-static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz,
-        jobject tokenObj, jint displayModeId, jfloat minRefreshRate, jfloat maxRefreshRate) {
+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, displayModeId, minRefreshRate, maxRefreshRate);
+            token, defaultModeId, minRefreshRate, maxRefreshRate);
     return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -1376,7 +1391,8 @@
             (void*)nativeSetAllowedDisplayConfigs },
     {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetAllowedDisplayConfigs },
-    {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;IFF)Z",
+    {"nativeSetDesiredDisplayConfigSpecs",
+            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
             (void*)nativeSetDesiredDisplayConfigSpecs },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
@@ -1547,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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b83294c..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
@@ -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/config.xml b/core/res/res/values/config.xml
index d66e0ce..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
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/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/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/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/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/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 367959e..9a7c373 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. */
@@ -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) {
@@ -532,6 +542,7 @@
         public void onClick(final View v) {
             mExpandIcon = v;
             toggleDialogExpansion(true);
+            rescheduleTimeoutH();
         }
     }
 
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/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/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/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/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/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/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/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 9d1eb6c..d4889ea 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -355,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;
 
@@ -1725,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 {
@@ -3752,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/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/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 6118df5..2df682f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -95,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;
@@ -311,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;
         }
     }
 
@@ -393,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();
         }
     }
@@ -432,7 +432,7 @@
     /**
      * Listens for changes to display mode coordination.
      */
-    public interface Listener {
+    public interface DisplayModeListener {
         /**
          * Called when the allowed display modes may have changed.
          */
@@ -448,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:
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 1586473..c4ea81a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -693,8 +693,9 @@
             }
 
             final IBinder token = getDisplayTokenLocked();
-            SurfaceControl.setDesiredDisplayConfigSpecs(token, defaultModeId, minRefreshRate,
-                    maxRefreshRate);
+            SurfaceControl.setDesiredDisplayConfigSpecs(token,
+                    new SurfaceControl.DesiredDisplayConfigSpecs(
+                            defaultModeId, minRefreshRate, maxRefreshRate));
             int activePhysIndex = SurfaceControl.getActiveConfig(token);
             return updateActiveModeLocked(activePhysIndex);
         }
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/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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 05f62b2..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;
             }
         }
 
@@ -3320,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.
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a593ef8..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,70 +585,76 @@
     /**
      * 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. */
@@ -552,8 +665,7 @@
     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.
@@ -565,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;
             }
 
@@ -579,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");
         }
     }
 
@@ -593,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);
@@ -794,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;
         }
@@ -825,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;
         }
@@ -853,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.
@@ -894,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;
         }
@@ -970,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;
@@ -1012,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.
@@ -1029,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() {
@@ -1088,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);
     }
@@ -1116,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);
 
@@ -1132,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 9e50dca..963e090 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -312,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;
@@ -3010,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;
@@ -4985,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,
@@ -5017,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,
@@ -5219,7 +5220,8 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                onStartingWindowDrawn(SystemClock.elapsedRealtimeNanos());
+                // The starting window for this container is drawn.
+                mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
                 startingDisplayed = true;
             }
         }
@@ -5227,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.
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0376d2c..d98aa6f 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -142,6 +142,7 @@
 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;
@@ -443,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();
@@ -576,8 +576,7 @@
     }
 
     void stopWaitingForActivityVisible(ActivityRecord r) {
-        stopWaitingForActivityVisible(r,
-                getActivityMetricsLogger().getLastDrawnDelayMs(r.getWindowingMode()));
+        stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
     }
 
     void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
@@ -2762,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 */);
@@ -2770,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/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a496396..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));
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 df03940..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;
@@ -1228,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;
     }
 
@@ -1254,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();
     }
 
 
@@ -6099,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/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f2e9505..f4c867c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -45,6 +45,7 @@
 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;
 
 /**
@@ -196,7 +197,8 @@
                     true /* forceSend */, targetActivity);
         }
 
-        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
+        final LaunchingState launchingState =
+                mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
 
         if (mCaller != null) {
             mCaller.setRunningRecentsAnimation(true);
@@ -255,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);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4c5ca38..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;
@@ -502,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() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3b13ac8..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);
@@ -6795,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);
         }
     }
 
@@ -6836,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);
         }
     }
 
@@ -6882,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/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/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/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/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/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/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/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/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 83bba27..52f5eee 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -723,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";
 
     /**